From 32aff4064fd3a0381b9ff56bf92e6cf6413562c3 Mon Sep 17 00:00:00 2001 From: Ted Moore Date: Mon, 25 Jul 2022 15:53:22 +0100 Subject: [PATCH 1/7] added link to examples folder in the guide --- .../HelpSource/Guides/FluidBufMultiThreading.schelp | 2 +- .../HelpSource/Guides/FluidCorpusManipulation.schelp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Guides/FluidBufMultiThreading.schelp b/release-packaging/HelpSource/Guides/FluidBufMultiThreading.schelp index 22cad5b..f15966b 100644 --- a/release-packaging/HelpSource/Guides/FluidBufMultiThreading.schelp +++ b/release-packaging/HelpSource/Guides/FluidBufMultiThreading.schelp @@ -192,7 +192,7 @@ Routine{ }.play; ) -//Danger zone – running directly in command FIFO: +//Blocking mode – running directly in command FIFO: ( Routine{ var startTime = Main.elapsedTime; diff --git a/release-packaging/HelpSource/Guides/FluidCorpusManipulation.schelp b/release-packaging/HelpSource/Guides/FluidCorpusManipulation.schelp index 92e2e29..7426ea7 100644 --- a/release-packaging/HelpSource/Guides/FluidCorpusManipulation.schelp +++ b/release-packaging/HelpSource/Guides/FluidCorpusManipulation.schelp @@ -8,6 +8,11 @@ The Fluid Corpus Manipulation toolkit provides an open-ended, loosely coupled se Almost all objects for audio analysis or transformation have audio-rate and buffer-based versions, and there are custom server-side containers for data analysis. footnote:: This toolbox was made possible thanks to the link::http://www.flucoma.org/##FluCoMa project:: funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: +Many useful examples can be found in the help files as well as in the example folder, which is here: +code:: +File.realpath(FluidFilesPath("../../Examples")).openOS; +:: + section::Contents list:: ##link::#Slice Audio:: From b0fbb30fb27ced5ebe06a1aca20d965f7842951a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 27 Jul 2022 12:04:20 +0100 Subject: [PATCH 2/7] (Buf)MFCC.sc: Handle maxNumBands (#130) --- release-packaging/Classes/FluidBufMFCC.sc | 6 +++--- release-packaging/Classes/FluidMFCC.sc | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index af29b39..6ab8fb1 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -8,7 +8,7 @@ FluidBufMFCC : FluidBufProcessor{ source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; - ^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking); + ^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action | @@ -23,7 +23,7 @@ FluidBufMFCC : FluidBufProcessor{ ^this.new( server, nil,[features] ).processList( - [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,0],freeWhenDone,action + [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,0],freeWhenDone,action ); } @@ -39,7 +39,7 @@ FluidBufMFCC : FluidBufProcessor{ ^this.new( server, nil,[features] ).processList( - [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,1],freeWhenDone,action + [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,1],freeWhenDone,action ); } } diff --git a/release-packaging/Classes/FluidMFCC.sc b/release-packaging/Classes/FluidMFCC.sc index b392fcd..e42cace 100644 --- a/release-packaging/Classes/FluidMFCC.sc +++ b/release-packaging/Classes/FluidMFCC.sc @@ -1,10 +1,11 @@ FluidMFCC : FluidRTMultiOutUGen { - *kr { arg in = 0, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumCoeffs = nil; + *kr { arg in = 0, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumCoeffs = nil, maxNumBands = nil; maxNumCoeffs = maxNumCoeffs ? numCoeffs; + maxNumBands = maxNumBands ? numBands; - ^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, maxNumCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize); + ^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, maxNumCoeffs, numBands, maxNumBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize); } @@ -18,6 +19,9 @@ FluidMFCC : FluidRTMultiOutUGen { if(inputs.at(2).rate != 'scalar') { ^(": maxNumCoeffs cannot be modulated."); }; + if(inputs.at(4).rate != 'scalar') { + ^(": maxNumBands cannot be modulated."); + }; if(inputs.at(10).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); }; From d0d5dfa9ec39d5a3bb894f9d8e76c07d8d673c71 Mon Sep 17 00:00:00 2001 From: Ted Moore Date: Wed, 27 Jul 2022 19:01:47 +0100 Subject: [PATCH 3/7] typo --- release-packaging/HelpSource/Classes/FluidWaveform.schelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidWaveform.schelp b/release-packaging/HelpSource/Classes/FluidWaveform.schelp index 7de7f4c..cfd72e7 100644 --- a/release-packaging/HelpSource/Classes/FluidWaveform.schelp +++ b/release-packaging/HelpSource/Classes/FluidWaveform.schelp @@ -285,7 +285,7 @@ FluidBufPitch.processBlocking(s,~audio,features:~pitch_analysis,action:{"done".p ~fw.layers.size ( -// plot differen components with different colors +// plot different components with different colors s.waitForBoot{ ~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); ~resynth = Buffer(s); From c54f43ad2e67f547cbc0de616453be34f6f95cca Mon Sep 17 00:00:00 2001 From: Ted Moore Date: Wed, 27 Jul 2022 19:03:35 +0100 Subject: [PATCH 4/7] Remove CondVar (#132) * removed from FluidWaveform * typo --- release-packaging/Classes/FluidWaveform.sc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/release-packaging/Classes/FluidWaveform.sc b/release-packaging/Classes/FluidWaveform.sc index 7fe993a..2e2d5b5 100644 --- a/release-packaging/Classes/FluidWaveform.sc +++ b/release-packaging/Classes/FluidWaveform.sc @@ -58,7 +58,7 @@ FluidWaveformIndicesLayer : FluidViewer { draw { var userView; - var condition = CondVar(); + var condition = Condition(); var slices_fa = nil; var numChannels = indicesBuffer.numChannels; @@ -81,9 +81,10 @@ FluidWaveformIndicesLayer : FluidViewer { indicesBuffer.loadToFloatArray(action: { arg v; slices_fa = v; - condition.signalOne; + condition.test = true; + condition.signal; }); - condition.wait { slices_fa.notNil }; + condition.wait; userView.drawFunc = numChannels.switch( 1, {{ @@ -132,7 +133,7 @@ FluidWaveformFeaturesLayer : FluidViewer { draw { var userView = UserView(); - var condition = CondVar(); + var condition = Condition(); var fa = nil; forkIfNeeded({ @@ -141,9 +142,10 @@ FluidWaveformFeaturesLayer : FluidViewer { featuresBuffer.loadToFloatArray(action:{ arg v; fa = v; - condition.signalOne + condition.test = true; + condition.signal; }); - condition.wait { fa.notNil }; + condition.wait; if(normalizeFeaturesIndependently.not,{ minVal = fa.minItem; @@ -209,7 +211,7 @@ FluidWaveformImageLayer { draw { var colors = this.prGetColorsFromScheme(imageColorScheme); - var condition = CondVar(); + var condition = Condition(); var vals = nil; var userView = UserView(); @@ -218,9 +220,10 @@ FluidWaveformImageLayer { imageBuffer.loadToFloatArray(action: { arg v; vals = v; - condition.signalOne; + condition.test = true; + condition.signal; }); - condition.wait { vals.notNil }; + condition.wait; imageColorScaling.switch( FluidWaveform.lin,{ From 7ee04f188c5619f5c790b8be5b4b33388795e0d1 Mon Sep 17 00:00:00 2001 From: weefuzzy Date: Mon, 8 Aug 2022 15:09:10 +0100 Subject: [PATCH 5/7] Remove redundant old help files --- .../HelpSource/Classes/FluidAmpGate.schelp | 136 ---- .../HelpSource/Classes/FluidAmpSlice.schelp | 86 --- .../Classes/FluidAudioTransport.schelp | 68 -- .../HelpSource/Classes/FluidBufAmpGate.schelp | 200 ------ .../Classes/FluidBufAmpSlice.schelp | 161 ----- .../Classes/FluidBufAudioTransport.schelp | 344 ---------- .../HelpSource/Classes/FluidBufChroma.schelp | 376 ----------- .../HelpSource/Classes/FluidBufCompose.schelp | 112 ---- .../HelpSource/Classes/FluidBufFlatten.schelp | 120 ---- .../HelpSource/Classes/FluidBufHPSS.schelp | 199 ------ .../Classes/FluidBufLoudness.schelp | 124 ---- .../HelpSource/Classes/FluidBufMFCC.schelp | 126 ---- .../Classes/FluidBufMelBands.schelp | 127 ---- .../HelpSource/Classes/FluidBufNMF.schelp | 629 ------------------ .../Classes/FluidBufNMFCross.schelp | 93 --- .../HelpSource/Classes/FluidBufNMFSeed.schelp | 100 --- .../Classes/FluidBufNoveltySlice.schelp | 179 ----- .../Classes/FluidBufOnsetSlice.schelp | 146 ---- .../HelpSource/Classes/FluidBufPitch.schelp | 193 ------ .../HelpSource/Classes/FluidBufSTFT.schelp | 109 --- .../HelpSource/Classes/FluidBufScale.schelp | 112 ---- .../HelpSource/Classes/FluidBufSelect.schelp | 62 -- .../Classes/FluidBufSelectEvery.schelp | 74 --- .../HelpSource/Classes/FluidBufSines.schelp | 148 ----- .../Classes/FluidBufSpectralShape.schelp | 142 ---- .../HelpSource/Classes/FluidBufStats.schelp | 288 -------- .../Classes/FluidBufThreadDemo.schelp | 71 -- .../HelpSource/Classes/FluidBufThresh.schelp | 81 --- .../Classes/FluidBufTransientSlice.schelp | 150 ----- .../Classes/FluidBufTransients.schelp | 149 ----- .../HelpSource/Classes/FluidChroma.schelp | 227 ------- .../HelpSource/Classes/FluidDataSet.schelp | 242 ------- .../Classes/FluidDataSetQuery.schelp | 185 ------ .../HelpSource/Classes/FluidGain.schelp | 38 -- .../HelpSource/Classes/FluidGrid.schelp | 201 ------ .../HelpSource/Classes/FluidHPSS.schelp | 114 ---- .../HelpSource/Classes/FluidKDTree.schelp | 177 ----- .../HelpSource/Classes/FluidKMeans.schelp | 259 -------- .../Classes/FluidKNNClassifier.schelp | 181 ----- .../Classes/FluidKNNRegressor.schelp | 131 ---- .../HelpSource/Classes/FluidLabelSet.schelp | 75 --- .../HelpSource/Classes/FluidLoudness.schelp | 161 ----- .../HelpSource/Classes/FluidMDS.schelp | 176 ----- .../HelpSource/Classes/FluidMFCC.schelp | 383 ----------- .../Classes/FluidMLPClassifier.schelp | 189 ------ .../Classes/FluidMLPRegressor.schelp | 191 ------ .../HelpSource/Classes/FluidMelBands.schelp | 207 ------ .../HelpSource/Classes/FluidNMFFilter.schelp | 234 ------- .../HelpSource/Classes/FluidNMFMatch.schelp | 249 ------- .../HelpSource/Classes/FluidNMFMorph.schelp | 93 --- .../HelpSource/Classes/FluidNormalize.schelp | 156 ----- .../Classes/FluidNoveltySlice.schelp | 85 --- .../HelpSource/Classes/FluidOnsetSlice.schelp | 85 --- .../HelpSource/Classes/FluidPCA.schelp | 169 ----- .../HelpSource/Classes/FluidPitch.schelp | 164 ----- .../Classes/FluidRobustScale.schelp | 158 ----- .../HelpSource/Classes/FluidSTFTPass.schelp | 61 -- .../HelpSource/Classes/FluidSines.schelp | 93 --- .../Classes/FluidSpectralShape.schelp | 325 --------- .../Classes/FluidStandardize.schelp | 150 ----- .../HelpSource/Classes/FluidStats.schelp | 47 -- .../Classes/FluidTransientSlice.schelp | 73 -- .../HelpSource/Classes/FluidTransients.schelp | 65 -- .../HelpSource/Classes/FluidUMAP.schelp | 247 ------- 64 files changed, 10496 deletions(-) delete mode 100644 release-packaging/HelpSource/Classes/FluidAmpGate.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidAmpSlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidAudioTransport.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufChroma.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufCompose.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufFlatten.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufHPSS.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufLoudness.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufMFCC.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufMelBands.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufNMF.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufNMFSeed.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufPitch.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufSTFT.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufScale.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufSelect.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufSelectEvery.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufSines.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufStats.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufThresh.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidBufTransients.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidChroma.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidDataSet.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidGain.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidGrid.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidHPSS.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidKDTree.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidKMeans.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidLabelSet.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidLoudness.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidMDS.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidMFCC.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidMelBands.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidNMFFilter.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidNMFMatch.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidNMFMorph.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidNormalize.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidNoveltySlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidPCA.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidPitch.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidRobustScale.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidSTFTPass.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidSines.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidSpectralShape.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidStandardize.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidStats.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidTransientSlice.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidTransients.schelp delete mode 100644 release-packaging/HelpSource/Classes/FluidUMAP.schelp diff --git a/release-packaging/HelpSource/Classes/FluidAmpGate.schelp b/release-packaging/HelpSource/Classes/FluidAmpGate.schelp deleted file mode 100644 index dce16ff..0000000 --- a/release-packaging/HelpSource/Classes/FluidAmpGate.schelp +++ /dev/null @@ -1,136 +0,0 @@ -TITLE:: FluidAmpGate -SUMMARY:: Amplitude-based Gating Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -DESCRIPTION:: -This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute amplitude changes as onsets and offsets. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -FluidAmpSlice is based on an envelop follower on a highpassed version of the signal, which is then going through a Schmidt trigger and state-aware time contraints. The example code below is unfolding the various possibilites in order of complexity. - -The process will return an audio steam with square envelopes around detected slices the different slices, where 1s means in slice and 0s mean in silence. - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: rampUp - The number of samples the envelope follower will take to reach the next value when raising. - -ARGUMENT:: rampDown - The number of samples the envelope follower will take to reach the next value when falling. - -ARGUMENT:: onThreshold - The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state. - -ARGUMENT:: offThreshold - The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state. - -ARGUMENT:: minSliceLength - The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. - -ARGUMENT:: minSilenceLength - The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. - -ARGUMENT:: minLengthAbove - The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. - -ARGUMENT:: minLengthBelow - The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. - -ARGUMENT:: lookBack - The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm. - -ARGUMENT:: lookAhead - The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm. - -ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. - -ARGUMENT:: maxSize - How large can the buffer be for time-critical conditions, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - An audio stream with square envelopes around the slices. The latency between the input and the output is STRONG::max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead))::. - -EXAMPLES:: - -code:: -//basic tests: threshold sanity -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12); - [source, env] -}.plot(0.1); -) -//basic tests: threshold hysteresis -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -16); - [source, env] -}.plot(0.1); -) -//basic tests: threshold min slice -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSliceLength:441); - [source, env] -}.plot(0.1); -) -//basic tests: threshold min silence -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSilenceLength:441); - [source, env] -}.plot(0.1); -) -//mid tests: threshold time hysteresis on -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthAbove:441); - [DelayN.ar(source,0.1,441/44100), env] -}.plot(0.1); -) -//mid tests: threshold time hysteresis off -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthBelow:441); - [DelayN.ar(source,0.1,441/44100), env] -}.plot(0.1); -) -//mid tests: threshold with lookBack -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:441); - [DelayN.ar(source,0.1,441/44100), env] -}.plot(0.1); -) -//mid tests: threshold with lookAhead -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookAhead:441); - [DelayN.ar(source,0.1,441/44100), env] -}.plot(0.1); -) -//mid tests: threshold with asymetrical lookBack and lookAhead -( -{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:221, lookAhead:441); - [DelayN.ar(source,0.1,441/44100), env] -}.plot(0.1); -) -//drum slicing, many ways -//load a buffer -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -//have fun with a gate (explore lookahead and lookback, but correct for latency, which will be the greatest of the lookahead and lookback) -( -{var env, source = PlayBuf.ar(1,b); - env = FluidAmpGate.ar(source, rampUp:441, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:4410, lookBack:441, highPassFreq:20); - [DelayN.ar(source,delaytime:441/44100), env] -}.plot(2, separately:true); -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp deleted file mode 100644 index cbad917..0000000 --- a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp +++ /dev/null @@ -1,86 +0,0 @@ -TITLE:: FluidAmpSlice -SUMMARY:: Amplitude-based Detrending Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -DESCRIPTION:: -This class implements an amplitude-based slicer, with various customisable options and conditions to detect relative amplitude changes as onsets. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -FluidAmpSlice is based on two envelop followers on a highpassed version of the signal: one slow that gives the trend, and one fast. Each have features that will interact. The example code below is unfolding the various possibilites in order of complexity. - -The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: fastRampUp - The number of samples the fast envelope follower will take to reach the next value when raising. Typically, this will be faster than slowRampUp. - -ARGUMENT:: fastRampDown - The number of samples the fast envelope follower will take to reach the next value when falling. Typically, this will be faster than slowRampDown. - -ARGUMENT:: slowRampUp - The number of samples the absolute envelope follower will take to reach the next value when raising. - -ARGUMENT:: slowRampDown - The number of samples the absolute envelope follower will take to reach the next value when falling. - -ARGUMENT:: onThreshold - The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers. - -ARGUMENT:: offThreshold - The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again. - -ARGUMENT:: floor - The level in dB the slowRamp needs to be above to consider a detected difference valid, allowing to ignore the slices in the noise floor. - -ARGUMENT:: minSliceLength - The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. - -ARGUMENT:: highPassFreq - The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##Linkwitz–Riley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. - -RETURNS:: - An audio stream with square envelopes around the slices. The latency between the input and the output is dependant on the relation between the two envelope followers. - -EXAMPLES:: - -code:: -// detrending explained -// Our source here is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track -( -{ - var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6)); - env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60); - [source, env] -}.plot(0.08); -) -//beware of double trigger at the begining of the 2nd cycle above). A solution: Schmidth triggers -( -{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6)); - env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60); - [source, env] -}.plot(0.08); -) -// another solution: minslicelength -( -{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6)); - env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 441); - [source, env] -}.plot(0.08); -) -//quick drum onsets -//load a buffer -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -( -{var env, source = PlayBuf.ar(1,b); - env = FluidAmpSlice.ar(source,fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410,onThreshold: 10,offThreshold: 5,floor: -40,minSliceLength: 4410,highPassFreq: 20); - [source, env] -}.play; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp deleted file mode 100644 index 77f9b60..0000000 --- a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp +++ /dev/null @@ -1,68 +0,0 @@ -TITLE:: FluidAudioTransport -summary:: Interpolate between sounds -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidBufAudioTransport - -DESCRIPTION:: -Interpolates between the spectra of two sounds using the Optimal Transport algorithm - -See -Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx - - -CLASSMETHODS:: - -METHOD:: ar -Process incoming audio signals - -ARGUMENT:: in -Source A - -ARGUMENT:: in2 -Source B - -ARGUMENT:: interpolation -The amount to interpolate between A and B (0-1, 0 = A, 1 = B) - -ARGUMENT:: windowSize - The window size in samples. As AudioTransport relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size in samples. As AudioTransport relies on spectral frames, we need to move the window forward. It can be any size but low overlap may create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The -1 default value will use the next power of 2 equal or above the windowSize. - - ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - An audio stream with the interpolated spectrum of the inputs. -EXAMPLES:: - -code:: -//didactic - the mouse X axis interpolates between the two sinewaves -{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr())}.play; - -//notice how the interpolation quantizes to the FFT bins. Like most spectral processes, it benefits from oversampling the fft... at the cost of CPU power, obviously. -{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr(),fftSize: 8192)}.play; - -// when the signal is steady, larger hopSize can be accommodated to save back on the CPU -{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr(),windowSize: 8192)}.play; // here we get a default hop of half the window so 8 times less than above. - -//if you CPU can cope, try this setting, almost smooth, but attacks would smear (the Y axis mixes some in to hear the effect) -{var attacks = Impulse.ar(1,mul: MouseY.kr(-40,10).dbamp); FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1,add: attacks),SinOsc.ar(440,mul: 0.02,add: attacks),MouseX.kr(),windowSize: 16000)}.play; - -//richer with complex spectra -//load 2 files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-CEL-GlitchyMusicBoxMelo.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-CF-ChurchBells.wav")); -) -//listen to them -b.play -c.play -//stereo cross! -{FluidAudioTransport.ar(PlayBuf.ar(2,b,loop: 1),PlayBuf.ar(2,c,loop: 1),MouseX.kr())}.play; - -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp deleted file mode 100644 index d5e8029..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp +++ /dev/null @@ -1,200 +0,0 @@ -TITLE:: FluidBufAmpGate -SUMMARY:: Amplitude-based Gating Slicer for Buffers -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -DESCRIPTION:: -This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute amplitude changes as onsets and offsets. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -FluidBufAmpGate is based on an envelop follower on a highpassed version of the signal, which is then going through a Schmidt trigger and state-aware time contraints. The example code below is unfolding the various possibilites in order of complexity. - -The process will return a two-channel buffer with the addresses of the onset on the first channel, and the address of the offset on the second channel. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking -This is the method that calls for the slicing to be calculated on a given source buffer. - -ARGUMENT:: server -The server on which the buffers to be processed are allocated. - -ARGUMENT:: source -The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. - -ARGUMENT:: startFrame -Where in the srcBuf should the slicing process start, in sample. - -ARGUMENT:: numFrames -How many frames should be processed. - -ARGUMENT:: startChan -For multichannel sources, which channel should be processed. - -ARGUMENT:: numChans -For multichannel sources, how many channel should be summed. - -ARGUMENT:: indices -The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. - -ARGUMENT:: rampUp -The number of samples the envelope follower will take to reach the next value when raising. - -ARGUMENT:: rampDown -The number of samples the envelope follower will take to reach the next value when falling. - -ARGUMENT:: onThreshold -The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state. - -ARGUMENT:: offThreshold -The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state. - -ARGUMENT:: minSliceLength -The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. - -ARGUMENT:: minSilenceLength -The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. - -ARGUMENT:: minLengthAbove -The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. - -ARGUMENT:: minLengthBelow -The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. - -ARGUMENT:: lookBack -The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm. - -ARGUMENT:: lookAhead -The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm. - -ARGUMENT:: highPassFreq -The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##Linkwitz–Riley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action -A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// define a test signal and a destination buffer -( - b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs})); - c = Buffer.new(s); -) -b.play -b.plot -//basic tests: threshold sanity -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//basic tests: threshold hysteresis -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -16) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//basic tests: threshold min slice -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSliceLength:441) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//basic tests: threshold min silence -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSilenceLength:441) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//mid tests: threshold time hysteresis on -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthAbove:441) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//mid tests: threshold time hysteresis off -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthBelow:441) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//mid tests: threshold with lookBack -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:441) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//mid tests: threshold with lookAhead -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookAhead:441) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) - -//mid tests: threshold with asymetrical lookBack and lookAhead -FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:221, lookAhead:441) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) -:: - -STRONG::A musical example.:: -CODE:: -//load a buffer -( - b = Buffer.read(s, FluidFilesPath("Nicol-LoopE-M.wav")); - c = Buffer.new(s); -) - -// slice the samples -( -Routine{ - FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40).wait; - c.query; - c.getn(0,c.numFrames*2,{|item|item.postln;}); - //reformatting to read the onsets and offsets as pairs - c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});}); -}.play -) -//loops over a splice with the MouseX, taking the respective onset and offset of a given slice -( - { - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(2, c, - MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0], - BufRd.kr(2, c, - MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1], - BufRd.kr(2,c, - MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0] - ), 0, 1); - }.play; -) -:: - -STRONG::A stereo buffer example.:: -CODE:: -// make a stereo buffer -b = Buffer.alloc(s,88200,2); - -// add some stereo clicks and listen to them -((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)}) -b.play - -// create a new buffer as destinations -c = Buffer.new(s); -//run the process on them -( - // with basic params - Routine{ - var t = Main.elapsedTime; - var proc= FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30); - proc.wait; - (Main.elapsedTime - t).postln; - }.play -) - -// list the indicies of detected attacks - the two input channels have been summed. The two channels of the output, respectively onset and offset indices, are interleaved as this is the SuperCollider buffer data formatting -c.getn(0,c.numFrames*2,{|item|(item*2).postln;}) -// a more readable version: deinterleave onsetand offset -c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| (x*2).postln});}) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp deleted file mode 100644 index 5ec566e..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp +++ /dev/null @@ -1,161 +0,0 @@ -TITLE:: FluidBufAmpSlice -SUMMARY:: Amplitude-based Detrending Slicer for Buffers -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -DESCRIPTION:: -This class implements an amplitude-based slicer,with various customisable options and conditions to detect relative amplitude changes as onsets. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -FluidBufAmpSlice is based on two envelop followers on a highpassed version of the signal: one slow that gives the trend, and one fast. Each have features that will interact. The example code below is unfolding the various possibilites in order of complexity. - -The process will return a buffer which contains indices (in sample) of estimated starting points of different slices. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the slicing to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. - -ARGUMENT:: startFrame - Where in the srcBuf should the slicing process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel sources, which channel should be processed. - -ARGUMENT:: numChans - For multichannel sources, how many channel should be summed. - -ARGUMENT:: indices - The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. - -ARGUMENT:: fastRampUp - The number of samples the fast envelope follower will take to reach the next value when raising. Typically, this will be faster than slowRampUp. - -ARGUMENT:: fastRampDown - The number of samples the fast envelope follower will take to reach the next value when falling. Typically, this will be faster than slowRampDown. - -ARGUMENT:: slowRampUp - The number of samples the absolute envelope follower will take to reach the next value when raising. - -ARGUMENT:: slowRampDown - The number of samples the absolute envelope follower will take to reach the next value when falling. - -ARGUMENT:: onThreshold - The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers. - -ARGUMENT:: offThreshold - The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again. - -ARGUMENT:: floor - The level in dB the slowRamp needs to be above to consider a detected difference valid, allowing to ignore the slices in the noise floor. - -ARGUMENT:: minSliceLength - The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. - -ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// detrending explained -// define a test signal and a destination buffer -( -b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * ((((79000-i) % 22050).abs / 28000.0) + 0.2)})); -c = Buffer.new(s); -) -// the source is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track -b.play -b.plot - -//process with symmetrical thresholds -FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60); -c.query -c.getn(0,c.numFrames,{|item|item.postln;}) - -//beware of multiple triggers at the begining of the 2nd cycle above). A solution: Schmidth triggers -FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60); -c.query -c.getn(0,c.numFrames,{|item|item.postln;}) - -// we got most of them sorted, but there is another solution: minslicelength -FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 500); -c.query -c.getn(0,c.numFrames,{|item|item.postln;}) -:: - -STRONG::A musical example.:: -CODE:: -//load a buffer -( -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -c = Buffer.new(s); -) - -// slice the samples -FluidBufAmpSlice.process(s,b,indices:c,fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410,onThreshold: 10,offThreshold: 5,floor: -40,minSliceLength: 4410,highPassFreq: 20); -c.query -c.getn(0,c.numFrames,{|item|item.postln;}) - -//loops over a splice with the MouseX -( -{ - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(1, c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1), - BufRd.kr(1, c, - MouseX.kr(1, BufFrames.kr(c)), 0, 1), - BufRd.kr(1,c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1); - }.play; -) -:: - -STRONG::A stereo buffer example.:: -CODE:: -// make a stereo buffer -b = Buffer.alloc(s,88200,2); - -// add some stereo clicks and listen to them -((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)}) -b.play - -// create a new buffer as destinations -c = Buffer.new(s); - -//run the process on them -( - // with basic params - Routine{ - var t = Main.elapsedTime; - var proc = FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5); - proc.wait; - c.query; - (Main.elapsedTime - t).postln; - }.play -) - -// list the indicies of detected attacks - the two input channels have been summed. -c.getn(0,c.numFrames,{|item|(item * 2).postln;}) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp deleted file mode 100644 index 50bd11a..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp +++ /dev/null @@ -1,344 +0,0 @@ -TITLE:: FluidBufAudioTransport -SUMMARY:: Interpolate between buffers -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Classes/FluidAudioTransport -DESCRIPTION:: - - - Interpolates between the spectra of two sounds using the Optimal Transport algorithm - - See Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx - - - - -CLASSMETHODS:: - -METHOD:: process, processBlocking - Processs the source LINK::Classes/Buffer:: on the LINK::Classes/Server::. CODE::processBlocking:: will execute directly in the server command FIFO, whereas CODE::process:: will delegate to a separate worker thread. The latter is generally only worthwhile for longer-running jobs where you don't wish to tie up the server. - -ARGUMENT:: server - The LINK::Classes/Server:: on which the buffers to be processed are allocated. - -ARGUMENT:: sourceA - - - The first source buffer - - -ARGUMENT:: startFrameA - - - offset into the first source buffer (samples) - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFramesA - - - number of samples to use from first source buffer - - -ARGUMENT:: startChanA - - - starting channel of first source buffer - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChansA - - - number of channels to process in first source buffer - - -ARGUMENT:: sourceB - - - the second source buffer - - -ARGUMENT:: startFrameB - - - offset into the second source buffer (samples) - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFramesB - - - number of samples to process from second buffer - - -ARGUMENT:: startChanB - - - starting channel for second buffer - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChansB - - - number of channels to process in second buffer - - -ARGUMENT:: destination - - - buffer for interpolated audio - - -ARGUMENT:: interpolation - - - The amount to interpolate between A and B (0-1, 0 = A, 1 = B) - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0.0 - - ## - Maximum: 1.0 - - :: - -ARGUMENT:: windowSize - - - The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. - - - -ARGUMENT:: freeWhenDone - Free the server instance when processing complete. Default CODE::true:: - -ARGUMENT:: action - A function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed CODE::[features]:: as an argument. - -RETURNS:: An instance of the processor - -METHOD:: kr - Trigger the equivalent behaviour to CODE::processBlocking / process:: from a LINK::Classes/Synth::. Can be useful for expressing a sequence of buffer and data processing jobs to execute. Note that the work still executes on the server command FIFO (not the audio thread), and it is the caller's responsibility to manage the sequencing, using the CODE::done:: status of the various UGens. -ARGUMENT:: sourceA - - - The first source buffer - - -ARGUMENT:: startFrameA - - - offset into the first source buffer (samples) - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFramesA - - - number of samples to use from first source buffer - - -ARGUMENT:: startChanA - - - starting channel of first source buffer - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChansA - - - number of channels to process in first source buffer - - -ARGUMENT:: sourceB - - - the second source buffer - - -ARGUMENT:: startFrameB - - - offset into the second source buffer (samples) - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFramesB - - - number of samples to process from second buffer - - -ARGUMENT:: startChanB - - - starting channel for second buffer - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChansB - - - number of channels to process in second buffer - - -ARGUMENT:: destination - - - buffer for interpolated audio - - -ARGUMENT:: interpolation - - - The amount to interpolate between A and B (0-1, 0 = A, 1 = B) - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0.0 - - ## - Maximum: 1.0 - - :: - -ARGUMENT:: windowSize - - - The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. - - - -ARGUMENT:: trig - A CODE::kr:: signal that will trigger execution - -ARGUMENT:: blocking - Whether to execute this process directly on the server command FIFO or delegate to a worker thread. See CODE::processBlocking/process:: for caveats. - - -INSTANCEMETHODS:: -METHOD:: kr - Returns a UGen that reports the progress of the running task when executing in a worker thread. Calling code::scope:: with this can be used for a convinient progress monitor - -METHOD:: cancel - Cancels non-blocking processing - -METHOD:: wait - When called in the context of a LINK::Classes/Routine:: (it won't work otherwise), will block execution until the processor has finished. This can be convinient for writing sequences of processes more linearly than using lots of nested actions. - -EXAMPLES:: - -code:: -//Didactic: -//Make 2 sinewave sources to be interpolated -( -b = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi).sin * 0.1})); -c = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi / 2).sin * 0.02})); -d = Buffer.new -) - -//make an sound interpolating their spectrum -FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,action:{"Ding".postln}) - -// listen to the source and the result -b.play -c.play -d.play - -// note that the process is quantized by the spectral bins. For an example of the pros and cons of these settings on this given process, please see the real-time FluidAudioTransport helpfile. - -// more interesting sources: two cardboard bowing gestures -( -b = Buffer.read(s,FluidFilesPath("Green-Box641.wav")); -c = Buffer.read(s,FluidFilesPath("Green-Box639.wav")); -d = Buffer.new -) - -// listen to the source -b.play -c.play - -// process and listen -FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,action:{"Ding".postln}) -d.play -// try various interpolation factors (0.1 and 0.9 are quite good -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufChroma.schelp b/release-packaging/HelpSource/Classes/FluidBufChroma.schelp deleted file mode 100644 index 6e85678..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufChroma.schelp +++ /dev/null @@ -1,376 +0,0 @@ -TITLE:: FluidBufChroma -SUMMARY:: A histogram of pitch classes on a Buffer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Classes/FluidChroma,Classes/FluidBufPitch,Classes/FluidBufLoudness,Classes/FluidBufMFCC,Classes/FluidBufSpectralShape,Classes/FluidBufStats,Guides/FluidCorpusManipulationToolkit,Classes/FluidBufMFCC -DESCRIPTION:: - - - This class computes a histogram of the energy contained for each pitch class across the analysis frequency range. - - - - Also known as a chromagram, this typically allows you to get a contour of how much each semitone is represented in the spectrum over time. The number of chroma bins (and, thus, pitch classes) and the central reference frequency can be adjusted. - - The process will return a single multichannel buffer of STRONG::numChroma:: per input channel. Each frame represents a value, which is every hopSize. - - -CLASSMETHODS:: - -METHOD:: process, processBlocking - Processs the source LINK::Classes/Buffer:: on the LINK::Classes/Server::. CODE::processBlocking:: will execute directly in the server command FIFO, whereas CODE::process:: will delegate to a separate worker thread. The latter is generally only worthwhile for longer-running jobs where you don't wish to tie up the server. - -ARGUMENT:: server - The LINK::Classes/Server:: on which the buffers to be processed are allocated. - -ARGUMENT:: source - - - The index of the buffer to use as the source material to be analysed. The different channels of multichannel buffers will be processing sequentially. - - -ARGUMENT:: startFrame - - - Where in the srcBuf should the process start, in sample. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFrames - - - How many frames should be processed. - - -ARGUMENT:: startChan - - - For multichannel srcBuf, which channel should be processed first. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChans - - - For multichannel srcBuf, how many channel should be processed. - - -ARGUMENT:: features - - - The destination buffer for the STRONG::numChroma:: to be written to. - - -ARGUMENT:: numChroma - - - The number of chroma bins per octave. It will determine how many channels are output per input channel. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 2 - - ## - Maximum: CODE::maxNumChroma:: - - :: - -ARGUMENT:: ref - - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - ## - Maximum: 22000 - - :: - -ARGUMENT:: normalize - - - This flag enables the scaling of the output. It is off (0) by default. (1) will normalise each frame to sum to 1. (2) normalises each frame relative to the loudest chroma bin being 1. - - -ARGUMENT:: minFreq - - - The lower frequency included in the analysis, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: maxFreq - - - The highest frequency included in the analysis, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: -1 - - :: - -ARGUMENT:: windowSize - - - The window size. As chroma description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As chroma description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. - - -ARGUMENT:: padding - - - Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - - - -ARGUMENT:: freeWhenDone - Free the server instance when processing complete. Default CODE::true:: - -ARGUMENT:: action - A function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed CODE::[features]:: as an argument. - -RETURNS:: An instance of the processor - -METHOD:: kr - Trigger the equivalent behaviour to CODE::processBlocking / process:: from a LINK::Classes/Synth::. Can be useful for expressing a sequence of buffer and data processing jobs to execute. Note that the work still executes on the server command FIFO (not the audio thread), and it is the caller's responsibility to manage the sequencing, using the CODE::done:: status of the various UGens. -ARGUMENT:: source - - - The index of the buffer to use as the source material to be analysed. The different channels of multichannel buffers will be processing sequentially. - - -ARGUMENT:: startFrame - - - Where in the srcBuf should the process start, in sample. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFrames - - - How many frames should be processed. - - -ARGUMENT:: startChan - - - For multichannel srcBuf, which channel should be processed first. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChans - - - For multichannel srcBuf, how many channel should be processed. - - -ARGUMENT:: features - - - The destination buffer for the STRONG::numChroma:: to be written to. - - -ARGUMENT:: numChroma - - - The number of chroma bins per octave. It will determine how many channels are output per input channel. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 2 - - ## - Maximum: CODE::maxNumChroma:: - - :: - -ARGUMENT:: ref - - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - ## - Maximum: 22000 - - :: - -ARGUMENT:: normalize - - - This flag enables the scaling of the output. It is off (0) by default. (1) will normalise each frame to sum to 1. (2) normalises each frame relative to the loudest chroma bin being 1. - - -ARGUMENT:: minFreq - - - The lower frequency included in the analysis, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: maxFreq - - - The highest frequency included in the analysis, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: -1 - - :: - -ARGUMENT:: windowSize - - - The window size. As chroma description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As chroma description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. - - -ARGUMENT:: padding - - - Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - - - -ARGUMENT:: trig - A CODE::kr:: signal that will trigger execution - -ARGUMENT:: blocking - Whether to execute this process directly on the server command FIFO or delegate to a worker thread. See CODE::processBlocking/process:: for caveats. - - -INSTANCEMETHODS:: -METHOD:: kr - Returns a UGen that reports the progress of the running task when executing in a worker thread. Calling code::scope:: with this can be used for a convinient progress monitor - -METHOD:: cancel - Cancels non-blocking processing - -METHOD:: wait - When called in the context of a LINK::Classes/Routine:: (it won't work otherwise), will block execution until the processor has finished. This can be convinient for writing sequences of processes more linearly than using lots of nested actions. - -EXAMPLES:: - -code:: -// create some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SlideChoirAdd-M.wav")); -c = Buffer.new(s); -) - -// run the process with basic parameters -( -Routine{ - t = Main.elapsedTime; - FluidBufChroma.process(s, b, features: c, windowSize: 4096).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// listen to the source and look at the buffer -b.play; -c.plot -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - -// create a buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufChroma.process(s, b, features: c, windowSize: 4096).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the buffer: 12 chroma bins for left, then 12 chroma bins for right -c.plot(separately:true) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp deleted file mode 100644 index 1cae066..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp +++ /dev/null @@ -1,112 +0,0 @@ -TITLE:: FluidBufCompose -SUMMARY:: Buffer Compositing Utility -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/Buffer - -DESCRIPTION:: -A FluidBufCompose object provides a flexible utility for combining the contents of buffers on the server. It can be used for thing like mixing down multichannel buffers, or converting from left-right stereo to mid-side. It is used extensively in all the example code of LINK::Guides/FluidCorpusManipulation:: as part of the FluCoMa project. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -At its most simple, the object copies the content of a source buffer into a destination buffer. The flexibility comes from the various flags controlling which portions and channels of the source to use, and by applying gains (which can be positive or negative) to the source data and the portion of the destination that would be overwritten. - -The algorithm takes a srcBuf, and writes the information at the provided dstBuf. These buffer arguments can all point to the same buffer, which gives great flexibility in transforming and reshaping. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This method triggers the compositing. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The bufNum of the source buffer. - -ARGUMENT:: startFrame - The starting point (in samples) from which to copy in the source buffer. - -ARGUMENT:: numFrames - The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer. - -ARGUMENT:: startChan - The first channel from which to copy in the source buffer. - -ARGUMENT:: numChans - The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel. - -ARGUMENT:: gain - The gain applied to the samples to be copied from the source buffer. - -ARGUMENT:: destination - The bufNum of the destination buffer. - -ARGUMENT:: destStartFrame - The time offset (in samples) in the destination buffer to start writing the source at. The destination buffer will be resized if the portion to copy is overflowing. - -ARGUMENT:: destStartChan - The channel offest in the destination buffer to start writing the source at. The destination buffer will be resized if the number of channels to copy is overflowing. - -ARGUMENT:: destGain - The gain applied to the samples in the region of the destination buffer over which the source is to be copied. The default value (0) will overwrite that section of the destination buffer, and a value of 1.0 would sum the source to the material that was present. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and destination instance variables have been updated on the client side. The function will be passed destination as an argument. - -returns:: an instance of the processor - -DISCUSSION:: - It is important to understand the rules used for determining the final desintinaiton buffer dimensions to get the most out of this object. If needs be, the destination buffer will be resized to the maxima of the requsted source numFrames and numChannels. Frames will be written up to the limit of actually available samples (meaning you can create zero padding); channels will be written modulo the available channels, taking into account the channel offsets, meaning you can have channels repeat or loop into the source buffer's channels. See the examples below. - -EXAMPLES:: - -code:: -// load some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -d = Buffer.new(s); -) - -// with basic params (basic summing of each full buffer in all dimensions) -( -Routine{ - FluidBufCompose.process(s, source: b, destination: d); - FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0); - s.sync; - d.query; - d.play; -}.play; -) -//constructing a mono buffer, with a quiet punch from the synth, with a choked piano resonance from the left channel -( -Routine{ - d.free; d = Buffer.new(s); - FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d); - FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0).wait; - d.query; - d.play; -}.play -) -//constructing a stereo buffer, with the end of the mono synth in both channels, with a piano resonance in swapped stereo -( -Routine{ - d.free; d = Buffer.new(s); - FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d); - FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0).wait; - d.query; - d.play; -}.play -) -//constructing a one second buffer: the first second of each buffer, the mono synth on the right, the piano on the left -( -Routine{ - d.free; d = Buffer.new(s); - FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d); - FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0).wait; - d.query; - d.play; -}.play -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp deleted file mode 100644 index 37f22da..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp +++ /dev/null @@ -1,120 +0,0 @@ -TITLE:: FluidBufFlatten -summary:: Flatten a multichannel buffer on the server -categories:: Libraries>FluidCorpusManipulation -related:: Classes/Buffer, Classes/FluidBufCompose, Classes/FluidBufSelect, Classes/FluidBufSelectEvery - -DESCRIPTION:: -Flatten a multichannel link::Classes/Buffer:: to a single channel. This can be useful for constructing n-dimensional data points for use with link::Classes/FluidDataSet:: - -The code::axis:: determines how the flattening is arranged. The default value, 1, flattens channel-wise, such that (if we imagine channels are rows, time positions are columns): - -table:: - ## a 1 || a 2 || a 3 - ## b 1 || b 2 || b 3 - ## c 1 || c 2 || c 3 -:: - -becomes - -table:: -## a 1 || b 1 || c 1 || a 2 || b 2 || c 2 || a 3 || b 3 || c 3 -:: - -whereas with code::axis = 0:: we get - -table:: - ## a 1 || a 2 || a 3 || b 1 || b 2 || b 3 || c 1 || c 2 || c 3 -:: - - -CLASSMETHODS:: - -private::new1 - -METHOD:: process, processBlocking - -Run the process on the given sever, and perfrom code::action:: when done - -ARGUMENT:: server -The link::Classes/Server:: on which to run - -ARGUMENT:: source -The link::Classes/Buffer:: to flatten - -ARGUMENT:: startFrame - Where in the source should the flattening process start, in samples. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel source buffers, which channel to start processing at. - -ARGUMENT:: numChans - For multichannel source buffers, how many channels should be processed. - -ARGUMENT:: destination -The link::Classes/Buffer:: to write the flattened data to - -ARGUMENT:: axis -Whether to group points channel-wise or frame-wise - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action -Runs when processing is complete - -EXAMPLES:: - -code:: -//FluidBufPitch is useful to illustrate the effect of this, because the pitch and confidence values are easily distinguishable - -( -~path = FluidFilesPath(); -~randomsoundfile = SoundFile.collect(~path +/+ '*').choose; -b = Buffer.read(s,~randomsoundfile.path,action:{"Sound Loaded".postln}); -~pitchdata = Buffer.new; -~flatdata = Buffer.new; -) - -//Pitch analysis, writes pitches as frequencies to chan 0, confidences [0-1] to chan 1 -FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,action:{"Pitch Analysis Done".postln}); - -// Flatten and print the flat buffer. We expect to see larger numbers (20-2000) interleaved with smaller (0-1) -( -FluidBufFlatten.process(s,~pitchdata, destination: ~flatdata, axis:1, action:{ - ~flatdata.loadToFloatArray(action:{ |a| - a.postln; - }) -}) -) - -//changing the axis, we see all large numbers first -( -FluidBufFlatten.process(s,~pitchdata, destination:~flatdata, axis:0, action:{ - ~flatdata.loadToFloatArray(action:{ |a| - a.postln; - }) -}) -) - -//adding the source range make this processor very powerful, but can be quite confusing -//here we take only one frame starting at the second one (0-counting) -( -FluidBufFlatten.process(s,~pitchdata,startFrame: 1,numFrames: 1, destination:~flatdata, action:{ - ~flatdata.loadToFloatArray(action:{ |a| - a.postln; - }) -}) -) -//and here we take only the confidences -( -FluidBufFlatten.process(s,~pitchdata, startChan: 1, destination:~flatdata, action:{ - ~flatdata.loadToFloatArray(action:{ |a| - a.postln; - }) -}) -) - -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp deleted file mode 100644 index 3e7ad4f..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp +++ /dev/null @@ -1,199 +0,0 @@ -TITLE:: FluidBufHPSS -SUMMARY:: Buffer-Based Harmonic-Percussive Source Separation Using Median Filtering -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - - -DESCRIPTION:: -A FluidBufHPSS object performs Harmonic-Percussive Source Separation (HPSS) on the contents of a link::Classes/Buffer::. The class performs HPSS as described in its original form footnote:: -Fitzgerald, Derry. 2010. ‘Harmonic/Percussive Separation Using Median Filtering’. In Proceedings DaFx 10. https://arrow.dit.ie/argcon/67. -:: as well as a variation on the extension propsoed by Driedger et al. footnote:: -Driedger, Jonathan, Meinard Uller, and Sascha Disch. 2014. ‘Extending Harmonic-Percussive Separation of Audio Signals’. In Proc. ISMIR. http://www.terasoft.com.tw/conf/ismir2014/proceedings/T110_127_Paper.pdf. -:: - -The algorithm takes a buffer in, and divides it into two or three outputs, depending on the mode: LIST:: - ## an harmonic component; - ## a percussive component; - ## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.:: - -It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking -This is the method that calls for the HPSS to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material. The channels of multichannel buffers will be processed sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the HPSS process start, in samples. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel to start processing at. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channels should be processed. - -ARGUMENT:: harmonic - The index of the buffer where the extracted harmonic component will be reconstructed. - -ARGUMENT:: percussive - The index of the buffer where the extracted percussive component will be reconstructed. - -ARGUMENT:: residual - The index of the buffer where the residual component will be reconstructed in mode 2. - -ARGUMENT:: harmFilterSize - The size, in spectral frames, of the median filter for the harmonic component. Must be an odd number, >= 3. - -ARGUMENT:: percFilterSize - The size, in spectral bins, of the median filter for the percussive component. Must be an odd number, >=3 - -ARGUMENT:: maskingMode - The way the masking is applied to the original spectrogram. (0,1,2) - table:: - ## 0 || The traditional soft mask used in Fitzgerald's original method of 'Wiener-inspired' filtering. Complimentary, soft masks are made for the harmonic and percussive parts by allocating some fraction of a point in time-frequency to each. This provides the fewest artefacts, but the weakest separation. The two resulting buffers will sum to exactly the original material. - ## 1 || Relative mode - Better separation, with more artefacts. The harmonic mask is constructed using a binary decision, based on whether a threshold is exceeded at a given time-frequency point (these are set using harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, see below). The percussive mask is then formed as the inverse of the harmonic one, meaning that as above, the two components will sum to the original sound. - - ## 2 || Inter-dependent mode - Thresholds can be varied independently, but are coupled in effect. Binary masks are made for each of the harmonic and percussive components, and the masks are converted to soft at the end so that everything null sums even if the params are independent, that is what makes it harder to control. These aren't guranteed to cover the whole sound; in this case the 'leftovers' will placed into a third buffer. - :: - -ARGUMENT:: harmThreshFreq1 - In modes 1 and 2, the frequency of the low part of the threshold for the harmonic filter (0-1) - -ARGUMENT:: harmThreshAmp1 - In modes 1 and 2, the threshold of the low part for the harmonic filter. That threshold applies to all frequencies up to harmThreshFreq1: how much more powerful (in dB) the harmonic median filter needs to be than the percussive median filter for this bin to be counted as harmonic. - -ARGUMENT:: harmThreshFreq2 - In modes 1 and 2, the frequency of the hight part of the threshold for the harmonic filter. (0-1) - -ARGUMENT:: harmThreshAmp2 - In modes 1 and 2, the threshold of the high part for the harmonic filter. That threshold applies to all frequencies above harmThreshFreq2. The threshold between harmThreshFreq1 and harmThreshFreq2 is interpolated between harmThreshAmp1 and harmThreshAmp2. How much more powerful (in dB) the harmonic median filter needs to be than the percussive median filter for this bin to be counted as harmonic. - -ARGUMENT:: percThreshFreq1 - In mode 2, the frequency of the low part of the threshold for the percussive filter. (0-1) - -ARGUMENT:: percThreshAmp1 - In mode 2, the threshold of the low part for the percussive filter. That threshold applies to all frequencies up to percThreshFreq1. How much more powerful (in dB) the percussive median filter needs to be than the harmonic median filter for this bin to be counted as percussive. - -ARGUMENT:: percThreshFreq2 - In mode 2, the frequency of the hight part of the threshold for the percussive filter. (0-1) - -ARGUMENT:: percThreshAmp2 - In mode 2, the threshold of the high part for the percussive filter. That threshold applies to all frequencies above percThreshFreq2. The threshold between percThreshFreq1 and percThreshFreq2 is interpolated between percThreshAmp1 and percThreshAmp2. How much more powerful (in dB) the percussive median filter needs to be than the harmonic median filter for this bin to be counted as percussive. - -ARGUMENT:: windowSize - The window size in samples. As HPSS relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size in samples. As HPSS relies on spectral frames, we need to move the window forward. It can be any size but low overlap may create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [harmonic, percussive, residual] as an argument. - -returns:: an instance of the processor - -Discussion:: - HPSS works by using median filters on the spectral magnitudes of a sound. It hinges on a simple modelling assumption that tonal components will tend to yield concentrations of energy across time, spread out in frequency, and percussive components will manifest as concentrations of energy across frequency, spread out in time. By using median filters across time and frequency respectively, we get initial esitmates of the tonal-ness / transient-ness of a point in time and frequency. These are then combined into 'masks' that are applied to the orginal spectral data in order to produce a separation. - - The maskingMode parameter provides different approaches to combinging estimates and producing masks. Some settings (especially in modes 1 & 2) will provide better separation but with more artefacts. These can, in principle, be ameliorated by applying smoothing filters to the masks before transforming back to the time-domain (not yet implemented). - -EXAMPLES:: - -code:: -//load buffers -( - b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); - c = Buffer.new(s); - d = Buffer.new(s); - e = Buffer.new(s); -) - -// run with basic parameters -( - Routine{ - t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: c, percussive: d).wait; - (Main.elapsedTime - t).postln; - }.play -) -c.query -d.query -//play the harmonic -c.play; -//play the percussive -d.play; - -//nullsumming tests -{(PlayBuf.ar(1,c))+(PlayBuf.ar(1,d))+(-1*PlayBuf.ar(1,b,doneAction:2))}.play - -//more daring parameters, in mode 2 -( -Routine{ - t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512) - .wait; - (Main.elapsedTime - t).postln; -}.play -) - -//play the harmonic -c.play; -//play the percussive -d.play; -//play the residual -e.play; - -//still nullsumming -{PlayBuf.ar(1,c) + PlayBuf.ar(1,d) + PlayBuf.ar(1,e) - PlayBuf.ar(1,b,doneAction:2)}.play; -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -( -Routine{ - FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b).wait; - b.play -}.play -) -// create 2 new buffers as destinations -d = Buffer.new(s); e = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: d, percussive:e).wait; - (Main.elapsedTime - t).postln; -}.play -) - -//listen: stereo preserved! -d.play -e.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp deleted file mode 100644 index 5755881..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp +++ /dev/null @@ -1,124 +0,0 @@ -TITLE:: FluidBufLoudness -SUMMARY:: A Loudness and True-Peak Descriptor on a Buffer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - - -DESCRIPTION:: -This class implements two loudness descriptors, computing the true peak of the signal as well as applying the filters proposed by broadcasting standards to emulate the perception of amplitude. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a multichannel buffer with two channels per input channel, one for loudness and one for the true peak value of the frame, both in dBfs. More information on broadcasting standardisation of loudness measurement is available at the reference page FOOTNOTE::https://tech.ebu.ch/docs/tech/tech3341.pdf:: and in more musician-friendly explantions here FOOTNOTE::http://designingsound.org/2013/02/06/loudness-and-metering-part-1/::. Each sample represents a value, which is every hopSize. Its sampling rate is STRONG::sourceSR / hopSize::. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the loudness descriptor to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be described. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed first. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be processed. - -ARGUMENT:: features - The destination buffer for the loudness descriptors. - -ARGUMENT:: kWeighting - A flag to switch the perceptual model of loudness. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple RMS of the frame. - -ARGUMENT:: truePeak - A flag to switch the computation of TruePeak. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple absolute peak of the frame. - -ARGUMENT:: windowSize - The size of the window on which the computation is done. By default 1024 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. - -ARGUMENT:: hopSize - How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. - -ARGUMENT:: padding - Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// create a buffer with a short clicking sinusoidal burst (220Hz) starting at frame 8192 for 1024 frames -( -b = Buffer.sendCollection(s, (Array.fill(8192,{0}) ++ (Signal.sineFill(1203,[0,0,0,0,0,1],[0,0,0,0,0,0.5pi]).takeThese({|x,i|i>1023})) ++ Array.fill(8192,{0}))); -c = Buffer.new(s); -) - -// listen to the source and look at the buffer -b.play; b.plot; - -// run the process with basic parameters -( -Routine{ - t = Main.elapsedTime; - FluidBufLoudness.process(s, source:b, features: c).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the analysis -c.plot(minval:-130, maxval:6) - -// The values are interleaved [loudness,truepeak] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopSize, then multiply by 2 because of the channel interleaving -// here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30. - -c.getn(30,10,{|x|x.postln}) - -// observe that the first frame is silent, as expected. We can appreciate the overshoot of TruePeak of a full range sinewave starting on the second sample (fourth item in the list). -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - -// create a buffer as destinations -c = Buffer.new(s); - -//run the process on them with EBU standard Instant Loudness of -( -Routine{ - t = Main.elapsedTime; - FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the buffer: [loudness,truepeak] for left then [loudness,truepeak] for right -c.plot(minval:-40, maxval:0) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp deleted file mode 100644 index b532a3c..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ /dev/null @@ -1,126 +0,0 @@ -TITLE:: FluidBufMFCC -SUMMARY:: Mel-Frequency Cepstral Coefficients as Spectral Descriptors on a Buffer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidBufMelBands - -DESCRIPTION:: -This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoeffs:: number of cepstral coefficients. It has the advantage of being amplitude invariant, except for the first coefficient. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a single multichannel buffer of STRONG::numCoeffs:: per input channel. Each frame represents a value, which is every hopSize. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed first. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be processed. - -ARGUMENT:: features - The destination buffer for the numCoeffs coefficients describing the spectral shape. - -ARGUMENT:: numCoeffs - The number of cepstral coefficients to be outputed. It will decide how many channels are produce per channel of the source. - -ARGUMENT:: numBands - The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. - -ARGUMENT:: startCoeff - The lowest index of the output cepstral coefficient, zero-counting. - -ARGUMENT:: minFreq - The lower boundary of the lowest band of the model, in Hz. - -ARGUMENT:: maxFreq - The highest boundary of the highest band of the model, in Hz. - -ARGUMENT:: windowSize - The window size. As MFCC computation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: padding - Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - -ARGUMENT:: freeWhenDone - Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// create some buffers -( -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -c = Buffer.new(s); -) - -// run the process with basic parameters -( -Routine{ - t = Main.elapsedTime; - FluidBufMFCC.process(s, b, features: c).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// listen to the source and look at the buffer -b.play; -c.plot(separately:true) -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - -// create a buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufMFCC.process(s, b, numCoeffs:5, features: c).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the buffer: 5 coefs for left, then 5 coefs for right (the first of each is linked to the loudness) -c.plot(separately:true) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp deleted file mode 100644 index f8384cb..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ /dev/null @@ -1,127 +0,0 @@ -TITLE:: FluidBufMelBands -SUMMARY:: A Perceptually Spread Spectral Contour Descriptor on a Buffer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidBufMFCC - - -DESCRIPTION:: -This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a single multichannel buffer of STRONG::numBands:: per input channel. Each frame represents a value, which is every hopSize. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed first. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be processed. - -ARGUMENT:: features - The destination buffer for the STRONG::numBands:: amplitudes describing the spectral shape. - -ARGUMENT:: numBands - The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. It will decide how many channels are produce per channel of the source. - -ARGUMENT:: minFreq - The lower boundary of the lowest band of the model, in Hz. - -ARGUMENT:: maxFreq - The highest boundary of the highest band of the model, in Hz. - -ARGUMENT:: normalize - This flag enables the scaling of the output to preserve the energy of the window. It is on (1) by default. - -ARGUMENT:: scale - This flag sets the scaling of the output value. It is either linear (0, by default) or in dB (1). - -ARGUMENT:: windowSize - The window size. As spectral description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: padding - Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// create some buffers -( -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -c = Buffer.new(s); -) - -// run the process with basic parameters -( -Routine{ - t = Main.elapsedTime; - FluidBufMelBands.process(s, b, features: c, numBands:10).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// listen to the source and look at the buffer -b.play; -c.plot -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - -// create a buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufMelBands.process(s, b, features: c, numBands:10).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the buffer: 10 bands for left, then 10 bands for right -c.plot(separately:true) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp deleted file mode 100644 index 3497c2b..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ /dev/null @@ -1,629 +0,0 @@ -TITLE:: FluidBufNMF -SUMMARY:: Buffer-Based Non-Negative Matrix Factorisation on Spectral Frames -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Classes/FluidNMFMatch,Classes/FluidNMFFilter,Guides/FluidCorpusManipulationToolkit,Classes/FluidNMFMatch,Classes/FluidNMFFilter -DESCRIPTION:: - - - Decomposes the spectrum of a sound into a number of components using Non-Negative Matrix Factorisation (NMF) - - - - NMF has been a popular technique in signal processing research for things like source separation and transcription (see Smaragdis and Brown, Non-Negative Matrix Factorization for Polyphonic Music Transcription.), although its creative potential is so far relatively unexplored. - - The algorithm takes a buffer in and divides it into a number of components, determined by the components argument. It works iteratively, by trying to find a combination of spectral templates ('bases') and envelopes ('activations') that yield the original magnitude spectrogram when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results. - - DEFINITIONLIST:: - ## The object can return either or all of the following: - || - LIST:: - ## - a spectral contour of each component in the form of a magnitude spectrogram (called a basis in NMF lingo); - - ## - an amplitude envelope of each component in the form of gains for each consecutive frame of the underlying spectrogram (called an activation in NMF lingo); - - ## - an audio reconstruction of each components in the time domain. - - :: - :: - The bases and activations can be used to make a kind of vocoder based on what NMF has 'learned' from the original data. Alternatively, taking the matrix product of a basis and an activation will yield a synthetic magnitude spectrogram of a component (which could be reconsructed, given some phase informaiton from somewhere). - - Some additional options and flexibility can be found through combinations of the basesMode and actMode arguments. If these flags are set to 1, the object expects to be supplied with pre-formed spectra (or envelopes) that will be used as seeds for the decomposition, providing more guided results. When set to 2, the supplied buffers won't be updated, so become templates to match against instead. Note that having both bases and activations set to 2 doesn't make sense, so the object will complain. - - DEFINITIONLIST:: - ## If supplying pre-formed data, it's up to the user to make sure that the supplied buffers are the right size: - || - LIST:: - ## - bases must be frames and channels - - ## - activations must be frames and channels - - :: - :: - In this implementation, the components are reconstructed by masking the original spectrum, such that they will sum to yield the original sound. - - The whole process can be related to a channel vocoder where, instead of fixed bandpass filters, we get more complex filter shapes that are learned from the data, and the activations correspond to channel envelopes. - - -CLASSMETHODS:: - -METHOD:: process, processBlocking - Processs the source LINK::Classes/Buffer:: on the LINK::Classes/Server::. CODE::processBlocking:: will execute directly in the server command FIFO, whereas CODE::process:: will delegate to a separate worker thread. The latter is generally only worthwhile for longer-running jobs where you don't wish to tie up the server. - -ARGUMENT:: server - The LINK::Classes/Server:: on which the buffers to be processed are allocated. - -ARGUMENT:: source - - - The index of the buffer to use as the source material to be decomposed through the NMF process. The different channels of multichannel buffers will be processing sequentially. - - -ARGUMENT:: startFrame - - - Where in the srcBuf should the NMF process start, in sample. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFrames - - - How many frames should be processed. - - -ARGUMENT:: startChan - - - For multichannel srcBuf, which channel should be processed first. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChans - - - For multichannel srcBuf, how many channel should be processed. - - -ARGUMENT:: resynth - - - The index of the buffer where the different reconstructed components will be reconstructed. The buffer will be resized to channels and lenght. If is provided, the reconstruction will not happen. - - -ARGUMENT:: bases - - - The index of the buffer where the different bases will be written to and/or read from: the behaviour is set in the following argument. If is provided, no bases will be returned. - - -ARGUMENT:: basesMode - - - This flag decides of how the basis buffer passed as the previous argument is treated. - - -ARGUMENT:: activations - - - The index of the buffer where the different activations will be written to and/or read from: the behaviour is set in the following argument. If is provided, no activation will be returned. - - -ARGUMENT:: actMode - - - This flag decides of how the activation buffer passed as the previous argument is treated. - - -ARGUMENT:: components - - - The number of elements the NMF algorithm will try to divide the spectrogram of the source in. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 1 - - :: - -ARGUMENT:: iterations - - - The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU expensive, lower numbers will be more unpredictable in quality. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 1 - - :: - -ARGUMENT:: windowSize - - - The window size. As NMF relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. - - - -ARGUMENT:: freeWhenDone - Free the server instance when processing complete. Default CODE::true:: - -ARGUMENT:: action - A function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed CODE::[features]:: as an argument. - -RETURNS:: An instance of the processor - -METHOD:: kr - Trigger the equivalent behaviour to CODE::processBlocking / process:: from a LINK::Classes/Synth::. Can be useful for expressing a sequence of buffer and data processing jobs to execute. Note that the work still executes on the server command FIFO (not the audio thread), and it is the caller's responsibility to manage the sequencing, using the CODE::done:: status of the various UGens. -ARGUMENT:: source - - - The index of the buffer to use as the source material to be decomposed through the NMF process. The different channels of multichannel buffers will be processing sequentially. - - -ARGUMENT:: startFrame - - - Where in the srcBuf should the NMF process start, in sample. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numFrames - - - How many frames should be processed. - - -ARGUMENT:: startChan - - - For multichannel srcBuf, which channel should be processed first. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: numChans - - - For multichannel srcBuf, how many channel should be processed. - - -ARGUMENT:: resynth - - - The index of the buffer where the different reconstructed components will be reconstructed. The buffer will be resized to channels and lenght. If is provided, the reconstruction will not happen. - - -ARGUMENT:: bases - - - The index of the buffer where the different bases will be written to and/or read from: the behaviour is set in the following argument. If is provided, no bases will be returned. - - -ARGUMENT:: basesMode - - - This flag decides of how the basis buffer passed as the previous argument is treated. - - -ARGUMENT:: activations - - - The index of the buffer where the different activations will be written to and/or read from: the behaviour is set in the following argument. If is provided, no activation will be returned. - - -ARGUMENT:: actMode - - - This flag decides of how the activation buffer passed as the previous argument is treated. - - -ARGUMENT:: components - - - The number of elements the NMF algorithm will try to divide the spectrogram of the source in. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 1 - - :: - -ARGUMENT:: iterations - - - The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU expensive, lower numbers will be more unpredictable in quality. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 1 - - :: - -ARGUMENT:: windowSize - - - The window size. As NMF relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. - - - -ARGUMENT:: trig - A CODE::kr:: signal that will trigger execution - -ARGUMENT:: blocking - Whether to execute this process directly on the server command FIFO or delegate to a worker thread. See CODE::processBlocking/process:: for caveats. - - -INSTANCEMETHODS:: -METHOD:: kr - Returns a UGen that reports the progress of the running task when executing in a worker thread. Calling code::scope:: with this can be used for a convinient progress monitor - -METHOD:: cancel - Cancels non-blocking processing - -METHOD:: wait - When called in the context of a LINK::Classes/Routine:: (it won't work otherwise), will block execution until the processor has finished. This can be convinient for writing sequences of processes more linearly than using lots of nested actions. - -EXAMPLES:: -STRONG::A didactic example:: -CODE:: - -// =============== decompose some sounds =============== - -// let's decompose the drum loop that comes with the FluCoMa extension: -~drums = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); - -// hear the original mono sound file to know what we're working with -~drums.play; - -// an empty buffer for the decomposed components to be written into: -~resynth = Buffer(s); - -// how many components we want FluidBufNMF to try to decompose the buffer into: -~n_components = 2; - -// process it: -FluidBufNMF.processBlocking(s,~drums,resynth:~resynth,components:~n_components,action:{"done".postln;}); - -// once it is done, play the separated components one by one (with a second of silence in between) -( -fork{ - ~n_components.do{ - arg i; - "decomposed part #%".format(i+1).postln; - { - PlayBuf.ar(~n_components,~resynth,BufRateScale.ir(~resynth),doneAction:2)[i].dup; - }.play; - (~drums.duration + 1).wait; - } -}; -) - -// ======== now let's try it with three components. ========= -// make a guess as to what you think you'll hear - -~n_components = 3; -// process it: -FluidBufNMF.processBlocking(s,~drums,resynth:~resynth,components:~n_components,action:{"done".postln;}); - -( -fork{ - ~n_components.do{ - arg i; - "decomposed part #%".format(i+1).postln; - { - PlayBuf.ar(~n_components,~resynth,BufRateScale.ir(~resynth),doneAction:2)[i].dup; - }.play; - (~drums.duration + 1).wait; - } -}; -) - -// you may have guessed that it would separate out the three components into: (1) snare, (2) hihat, and (3) kick -// and it might have worked! but it may not have, and it won't provide the same result every time because it -// starts each process from a stochastic state (you can seed this state if you want...see below). - -// ====== bases and activations ======== - -// first, let's make two new buffers called... -~bases = Buffer(s); -~activations = Buffer(s); -~n_components = 2; // return to 2 components for this example - -// and we'll explicitly pass these into the process -FluidBufNMF.processBlocking(s,~drums,bases:~bases,activations:~activations,resynth:~resynth,components:~n_components,action:{"done".postln;}); - -// now we can plot them (because this process starts from a stochastic state, your results may vary!): -FluidWaveform(~drums,featureBuffer:~activations,bounds:Rect(0,0,1200,300)); -// the bases are a like a spectral template that FluidBufNMF has found in the source buffer -// in one you should see one spectrum that resembles a snare spectrum (the resonant tone of the snare -// in the mid range) and another that resembles the kick + hihat we heard earlier (a large peak in the very -// low register and some shimmery higher stuff) - -FluidWaveform(featureBuffer:~bases,bounds:Rect(0,0,1200,300)); -// the activations are the corresponding loudness envelope of each base above. It should like an amplitude -// envelope follower of the drum hits in the corresponding bases. - -// FluidBufNMF then uses the individual bases with their corresponding activations to resynthesize the sound of just -// component. -// the buffer passed to `resynth` will have one channel for each component you've requested - -~resynth.numChannels -~resynth.play; - -// ======== to further understand NMF's bases and activations, consider one more object: FluidNMFFilter ========== -// FluidNMFFilter will use the bases (spectral templates) of a FluidBufNMF analysis to filter (i.e., decompose) real-time audio - -// for example, if we use the bases from the ~drums analysis above, it will separate the snare from the kick & hi hat like before -// this time you'll hear one in each stereo channel (again, results may vary) - -( -{ - var src = PlayBuf.ar(1,~drums,BufRateScale.ir(~drums),doneAction:2); - var sig = FluidNMFFilter.ar(src,~bases,2); - sig; -}.play; -) - -// if we play a different source through FluidNMFFilter, it will try to decompose that real-time signal according to the bases -// it is given (in our case the bases from the drum loop) -~song = Buffer.readChannel(s,FluidFilesPath("Tremblay-BeatRemember.wav"),channels:[0]); - -( -{ - var src = PlayBuf.ar(1,~song,BufRateScale.ir(~song),doneAction:2); - var sig = FluidNMFFilter.ar(src,~bases,2); - sig; -}.play; -) - -// ========= the activations could also be used as an envelope through time =========== -( -{ - var activation = PlayBuf.ar(2,~activations,BufRateScale.ir(~activations),doneAction:2); - var sig = WhiteNoise.ar(0.dbamp) * activation; - sig; -}.play; -) - -// note that the samplerate of the ~activations buffer is not a usual one... -~activations.sampleRate; -// this is because each frame in this buffer doesn't correspond to one audio sample, but instead to one -// hopSize, since these values are derived from an FFT analysis -// so it is important to use BufRateScale (as seen above) in order to make sure they play back at the -// correct rate - -// if we control the amplitude of the white noise *and* send it through FluidNMFFilter, we'll get something -// somewhat resembles both the spectral template and loudness envelope of the bases of the original -// (of course it's also good to note that the combination of the *actual* bases and activations is how -// FluidBufNMF creates the channels in the resynth buffer which will sound much better than this -// filtered WhiteNoise version) -( -{ - var activation = PlayBuf.ar(2,~activations,BufRateScale.ir(~activations),doneAction:2); - var sig = WhiteNoise.ar(0.dbamp); - sig = FluidNMFFilter.ar(sig,~bases,2) * activation; - sig; -}.play; -) - -:: -STRONG::Fixed Bases: The process can be trained, and the learnt bases or activations can be used as templates.:: - -CODE:: - -//set some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); - -~originalNMF = Buffer.new(s); -~bases = Buffer.new(s); -~trainedBases = Buffer.new(s); -~activations = Buffer.new(s); -~final = Buffer.new(s); -~spectralshapes = Buffer.new(s); -~stats = Buffer.new(s); -~sortedNMF = Buffer.new(s); -) - -b.play - -// train using the first 2 seconds of the sound file -( -Routine { - FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10).wait; - ~originalNMF.query; -}.play; -) - -// listen to the 10 components across the stereo image -{Splay.ar(PlayBuf.ar(10, ~originalNMF))}.play - -// plot the bases -~bases.plot - -// find the component that has the picking sound by checking the median spectral centroid -( -FluidBufSpectralShape.process(s, ~originalNMF, features: ~spectralshapes, action:{ - |shapes|FluidBufStats.process(s,shapes,stats:~stats, action:{ - |stats|stats.getn(0, (stats.numChannels * stats.numFrames) ,{ - |x| ~centroids = x.select({ - |item, index| (index.mod(7) == 0) && (index.div(70) == 5); - }) - }) - }) -}); -) - -//what is happening there? We run the spectralshapes on the buffer of 10 components from the nmf. See the structure of that buffer: -~originalNMF.query -//10 channel are therefore giving 70 channels: the 7 shapes of component0, then 7 shapes of compoenent1, etc -~spectralshapes.query -// we then run the bufstats on them. Each channel, which had a time series (an envelope) of each descriptor, is reduced to 7 frames -~stats.query -// we then need to retrieve the values that are where we want: the first of every 7 for the centroid, and the 6th frame of them as we want the median. Because we retrieve the values in an interleave format, the select function gets a bit tricky but we get the following values: -~centroids.postln - -// we then copy the basis with the highest median centroid to a channel, and all the other bases to the other channel, of a 2-channel bases for decomposition -( -z = (0..9); -[z.removeAt(~centroids.maxIndex)].do{|chan|FluidBufCompose.process(s, ~bases, startChan: chan, numChans: 1, destination: ~trainedBases, destGain:1)}; -z.postln; -z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, destStartChan: 1, destination: ~trainedBases, destGain:1)}); -) -~trainedBases.plot - -//process the whole file, splitting it with the 2 trained bases -( -Routine{ - FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2).wait; - ~originalNMF.query; -}.play; -) - -// play the result: pick on the left, sustain on the right! -{PlayBuf.ar(2,~sortedNMF)}.play - -// it even null-sums -{(PlayBuf.ar(2,~sortedNMF,doneAction:2).sum)-(PlayBuf.ar(1,b,doneAction:2))}.play -:: - -STRONG::Updating Bases: The process can update bases provided as seed.:: - -CODE:: -( -// create buffers -b = Buffer.alloc(s,44100); -c = Buffer.alloc(s, 44100); -d = Buffer.new(s); -e = Buffer.alloc(s,513,3); -f = Buffer.new(s); -g = Buffer.new(s); -) - -( -// fill them with 2 clearly segregated sine waves and composite a buffer where they are consecutive -Routine { - b.sine2([500],[1], false, false); - c.sine2([5000],[1],false, false); - s.sync; - FluidBufCompose.process(s,b, destination:d); - FluidBufCompose.process(s,c, destStartFrame:44100, destination:d, destGain:1); - s.sync; - d.query; -}.play; -) - -// check -d.plot -d.play //////(beware !!!! loud!!!) - -( -//make a seeding basis of 3 components: -var highpass, lowpass, direct; -highpass = Array.fill(513,{|i| (i < 50).asInteger}); -lowpass = 1 - highpass; -direct = Array.fill(513,0.1); -e.setn(0,[highpass, lowpass, direct].flop.flat); -) - -//check the basis: a steep lowpass, a steep highpass, and a small DC -e.plot -e.query - -( -// use the seeding basis, without updating -Routine { - FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3).wait; - e.query; - f.query; - g.query; -}.play -) - -// look at the resynthesised separated signal -f.plot; - -// look at the bases that have not changed -e.plot; - -// look at the activations -g.plot; - -( -// use the seeding bases, with updating this time -Routine { - FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3).wait; - e.query; - f.query; - g.query; -}.play -) - -// look at the resynthesised separated signal -f.plot; - -// look at the bases that have now updated in place (with the 3rd channel being more focused -e.plot; - -// look at the activations (sharper 3rd component at transitions) -g.plot; -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp b/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp deleted file mode 100644 index c11249c..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp +++ /dev/null @@ -1,93 +0,0 @@ -TITLE:: FluidBufNMFCross -summary:: Reconstructs the sound in the target buffer using components learned from the source buffer using an NMF decomposition -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidBufNMF, Classes/FluidNMFMatch, Classes/FluidNMFFilter - -DESCRIPTION:: - -The process works by attempting to reconstruct compoentns of the code::target:: sound using the timbre of the code::source:: sound, learned through a Nonnegative Matrix Factorisation. The result is a hybrid whose character depends on how well the target can be represnted by the source's spectral frames. - -In contrast to link::Classes/FluidBufNMF::, the size and content of the bases dictionary are fixed in this application to be the spectrogram of the `source`. Each spectral frame of `source` is a template: be aware that NMF is O(N^2) in the number of templates, so longer `source` link::Classes/Buffer::s will take dramatically longer to process. - -See Driedger, J., Prätzlich, T., & Müller, M. (2015). Let it Bee-Towards NMF-Inspired Audio Mosaicing. ISMIR, 350–356. http://ismir2015.uma.es/articles/13_Paper.pdf - -CLASSMETHODS:: - -private:: kr, new1 - -METHOD:: process, processBlocking - -Process two buffers. code::process:: will use its own worker thread on the server, and so avoid blocking the command FIFO queue. For very small jobs, it may be quicker to use code::processBlocking::, which runs directly in the server's queue. - -ARGUMENT:: server -The link::Classes/Server:: on which to process - -ARGUMENT:: source -A link::Classes/Buffer:: whose content will supply the spectral bases used in the hybrid - -ARGUMENT:: target -A link::Classes/Buffer:: whose content will supply the temporal activations used in the hybrid - -ARGUMENT:: output -A link::Classes/Buffer:: to contain the new sound - -ARGUMENT:: timeSparsity -Control the repetition of source templates in the reconstruction by specifying a number of frames within which a template should not be re-used. Units are spectral frames. - -ARGUMENT:: polyphony -Control the spectral density of the output sound by restricting the number of simultaneous templates that can be used. Units are spectral bins. - -ARGUMENT:: continuity -Promote the use of N successive source frames, giving greater continuity in the result. This can not be bigger than the sizes of the input buffers, but useful values tend to be much lower (in the tens). - -ARGUMENT:: iterations -How many iterations of NMF to run - -ARGUMENT:: windowSize -The analysis window size in samples - -ARGUMENT:: hopSize -The analysus hop size in samples (default winSize / 2) - -ARGUMENT:: fftSize -The analsyis FFT size in samples (default = winSize) - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action -A function to run when processing is complete, taking the output buffer as its argument - -returns:: an instance of the processor - -INSTANCEMETHODS:: - - -EXAMPLES:: - -code:: - -~path = FluidFilesPath(); -b = Buffer.read(s,~path+/+"Nicol-LoopE-M.wav") -t = Buffer.read(s,~path+/+"Tremblay-SA-UprightPianoPedalWide.wav") -o = Buffer.new -FluidBufNMFCross.process(s,t,b,o,windowSize: 2048, action:{"Ding".postln}) -//wait for it to be done. It can take a while, depending on the length of your source. -o.play - -//The result of the cross synthesis is a hybrid of the source and target sounds. The algorithm tries to match the target spectrum over time using components learned from the source. These parameters affect the reconstruction: -~sparsity = 4; //Avoid reusing a component from the source for this number of time frames -~polyphony = 3; //Avoid overlapping more than this number of source components at the same time -~continuity = 20; //Encourage the reconstruction to use this many temporally consecutive frames from the source - -//Using the UGen to run the process can be useful to monitor its progress -( -Routine{ - ~cross = FluidBufNMFCross.process(s,t,b,o,timeSparsity: ~sparsity, polyphony: ~polyphony, continuity: ~continuity, windowSize: 2048); - defer{{FreeSelfWhenDone.kr(~cross.kr).poll}.play;}; - ~cross.wait; - \Done.postln; -}.play; -) -o.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufNMFSeed.schelp b/release-packaging/HelpSource/Classes/FluidBufNMFSeed.schelp deleted file mode 100644 index 38f1348..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufNMFSeed.schelp +++ /dev/null @@ -1,100 +0,0 @@ -TITLE:: FluidBufNMFSeed -summary:: Non-Negative Double Singular Value Decomposition on a Buffer -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidBufNMF - -DESCRIPTION:: -Find Initial Bases and Activations for FluidBufNMF via Non-Negative Double Singular Value Decomposition . - -See http://nimfa.biolab.si/nimfa.methods.seeding.nndsvd.html - -CLASSMETHODS:: - -METHOD:: process, processBlocking -This is the method that calls for the decomposition to be calculated on a given source buffer. - -ARGUMENT:: server -The server on which the buffers to be processed are allocated. - -ARGUMENT:: source -The index of the buffer to use as the source material to be decomposed through the NMF process. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: bases -The index of the buffer where the different bases will be written to and/or read from: the behaviour is set in the following argument. - -ARGUMENT:: activations -The index of the buffer where the different activations will be written to and/or read from: the behaviour is set in the following argument. - -ARGUMENT:: minComponents -Minimum number of estimated components - -ARGUMENT:: maxComponents -Maximum number of estimated components - -ARGUMENT:: coverage -Fraction (0 to 1) of information preserved in the decomposition - -ARGUMENT:: method -The method used for the decomposition. Options are: - -table:: -## 0 || NMF-SVD || faster -## 1 || NNDSVDar || more accurate, fill in the zero elements with random values -## 2 || NNDSVDa || fill in the zero elements with the average -## 3 || NNDSVD || leave zero elements as zero, works better for sparse spectrograms -:: - -ARGUMENT:: windowSize - The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination] as an argument. - -returns:: an instance of the processor - - -INSTANCEMETHODS:: - -private:: synth, server - -EXAMPLES:: - -code:: -( -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -~bases = Buffer.new(s); -~activations = Buffer.new(s); -~resynth = Buffer.new(s); -) - -//how many bases do I need to decompose the buffer with 90% accuracy -( -Routine{ - FluidBufNMFSeed.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1).wait; - "% bases".format(~bases.numChannels).postln; -}.play; -) -//check how many bases we are returned: - - -//try the same process with less accuracy -( -Routine{ - FluidBufNMFSeed.process(s, b, ~bases, ~activations, coverage: 0.5).wait; - "% bases".format(~bases.numChannels).postln; -}.play -) - -//use the bases to run NMF on -FluidBufNMF.process(s, b, resynth: ~resynth, bases: ~bases, activations: ~activations,actMode: 2, components: ~bases.numChannels, action: {\done.postln;}) -{PlayBuf.ar(~resynth.numChannels, ~resynth)[2]}.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp deleted file mode 100644 index 5fc6da7..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ /dev/null @@ -1,179 +0,0 @@ -TITLE:: FluidBufNoveltySlice -SUMMARY:: Buffer-Based Novelty-Based Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -DESCRIPTION:: -This class implements a non-real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a buffer which contains indices (in sample) of estimated starting points of different slices. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking -This is the method that calls for the slicing to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. - -ARGUMENT:: startFrame - Where in the srcBuf should the slicing process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be summed. - -ARGUMENT:: indices - The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. - -ARGUMENT:: feature - The feature on which novelty is computed. - table:: - ##0 || Spectrum || The magnitude of the full spectrum. - ##1 || MFCC || 13 Mel-Frequency Cepstrum Coefficients. - ##2 || Chroma || The contour of a 12 band Chromagram. - ##3 || Pitch || The pitch and its confidence. - ##4 || Loudness || The TruePeak and Loudness. -:: -ARGUMENT:: kernelSize - The granularity of the window in which the algorithm looks for change, in samples. A small number will be sensitive to short term changes, and a large number should look for long term changes. - -ARGUMENT:: threshold - The normalised threshold, between 0 an 1, on the novelty curve to consider it a segmentation point. - -ARGUMENT:: filterSize - The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes. - -ARGUMENT:: minSliceLength - The minimum duration of a slice in number of hopSize. - -ARGUMENT:: windowSize - The window size. As novelty estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument. - -returns:: an instance of the processor - - -EXAMPLES:: - -code:: -// load some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -c = Buffer.new(s); -) - -( -// with basic params, with a minimum slight length to avoid over -Routine{ - t = Main.elapsedTime; - FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8).wait; - (Main.elapsedTime - t).postln; -}.play -) - -//check the number of slices: it is the number of frames in the transBuf minus the boundary index. -c.query; - -//loops over a splice with the MouseX -( -{ - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(1, c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1), - BufRd.kr(1, c, - MouseX.kr(1, BufFrames.kr(c)), 0, 1), - BufRd.kr(1,c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1); - }.play; -) - :: - -STRONG::Examples of the impact of the filterSize:: - - CODE:: -// load some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -c = Buffer.new(s); -) - -// process with a given filterSize -( -Routine{ - FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1).wait; -//check the number of slices: it is the number of frames in the transBuf minus the boundary index. - c.query; -}.play; -) -//play slice number 3 -( -{ - BufRd.ar(1, b, - Line.ar( - BufRd.kr(1, c, DC.kr(3), 0, 1), - BufRd.kr(1, c, DC.kr(4), 0, 1), - (BufRd.kr(1, c, DC.kr(4)) - BufRd.kr(1, c, DC.kr(3), 0, 1) + 1) / s.sampleRate), - 0,1); -}.play; -) - -// change the filterSize in the code above to 4. Then to 12. Listen in between to the differences. - -// What's happening? In the first instance (filterSize = 1), the novelty line is jittery and therefore overtriggers on the arpegiated guitar. We also can hear attacks at the end of the segment. Setting the threshold higher (like in the 'Basic Example' pane) misses some more subtle variations. - -// So in the second settings (filterSize = 4), we smooth the novelty line a little, which allows us to catch small differences that are not jittery. It also corrects the ending cutting by the same trick: the averaging of the sharp pick is sliding up, crossing the threshold slightly earlier. - -// If we smooth too much, like the third settings (filterSize = 12), we start to loose precision and miss attacks. Have fun with different values of theshold then will allow you to find the perfect segment for your signal. -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// make a stereo buffer -b = Buffer.alloc(s,88200,2); - -// add some stereo clicks and listen to them -((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)}); -b.play - -// create a new buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// list the indicies of detected attacks - the two input channels have been summed -c.getn(0,c.numFrames,{|item|(item * 2).postln;}) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp deleted file mode 100644 index 61a8679..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp +++ /dev/null @@ -1,146 +0,0 @@ -TITLE:: FluidBufOnsetSlice -SUMMARY:: Spectral Difference-Based Audio Buffer Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -DESCRIPTION:: -This class implements many spectral-based onset detection metrics, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object yet not as offline processes. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a buffer which contains indices (in samples) of estimated starting points of different slices. If a startFrame argument is supplied, the starting point indices are still specified in terms of their location in the original buffer; they are not offset by the number startFrames. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the slicing to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. - -ARGUMENT:: startFrame - Where in the srcBuf should the slicing process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel sources, which channel should be processed. - -ARGUMENT:: numChans - For multichannel sources, how many channel should be summed. - -ARGUMENT:: indices - The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. - -ARGUMENT:: metric - The metric used to derive a difference curve between spectral frames. It can be any of the following: - TABLE:: - ##0 || Energy || thresholds on (sum of squares of magnitudes / nBins) (like Onsets \power) - ##1 || HFC || thresholds on (sum of (squared magnitudes * binNum) / nBins) - ##2 || SpectralFlux || thresholds on (diffence in magnitude between consecutive frames, half rectified) - ##3 || MKL || thresholds on (sum of log of magnitude ratio per bin) (or equivalent: sum of difference of the log magnitude per bin) (like Onsets \mkl) - ##4 || IS || (WILL PROBABLY BE REMOVED) Itakura - Saito divergence (see literature) - ##5 || Cosine || thresholds on (cosine distance between comparison frames) - ##6 || PhaseDev || takes the past 2 frames, projects to the current, as anticipated if it was a steady state, then compute the sum of the differences, on which it thresholds (like Onsets \phase) - ##7 || WPhaseDev || same as PhaseDev, but weighted by the magnitude in order to remove chaos noise floor (like Onsets \wphase) - ##8 || ComplexDev || same as PhaseDev, but in the complex domain - the anticipated amp is considered steady, and the phase is projected, then a complex subtraction is done with the actual present frame. The sum of magnitudes is used to threshold (like Onsets \complex) - ##9 || RComplexDev || same as above, but rectified (like Onsets \rcomplex) - :: - -ARGUMENT:: threshold -The thresholding of a new slice. Value ranges are different for each metric, from 0 upwards. - -ARGUMENT:: minSliceLength - The minimum duration of a slice in number of hopSize. - -ARGUMENT:: filterSize - The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes. - -ARGUMENT:: frameDelta - For certain metrics (HFC, SpectralFlux, MKL, Cosine), the distance does not have to be computed between consecutive frames. By default (0) it is, otherwise this sets the distance between the comparison window in samples. - -ARGUMENT:: windowSize - The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As spectral differencing relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -CODE:: -( -//prep some buffers -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -c = Buffer.new(s); -) - -( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5).wait; - (Main.elapsedTime - t).postln; -}.play -) - -//check the number of slices: it is the number of frames in the transBuf minus the boundary index. -c.query; - -//loops over a splice with the MouseX -( -{ - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(1, c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1), - BufRd.kr(1, c, - MouseX.kr(1, BufFrames.kr(c)), 0, 1), - BufRd.kr(1,c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1); - }.play; -) -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// make a stereo buffer -b = Buffer.alloc(s,88200,2); - -// add some stereo clicks and listen to them -((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)}) -b.play - -// create a new buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// list the indicies of detected attacks - the two input channels have been summed -c.getn(0,c.numFrames,{|item|(item * 2).postln;}) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp deleted file mode 100644 index 6a67da4..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ /dev/null @@ -1,193 +0,0 @@ -TITLE:: FluidBufPitch -SUMMARY:: A Selection of Pitch Descriptors on a Buffer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile - - -DESCRIPTION:: -This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. A pitch of 0 Hz is yield (or -999.0 when the unit is in MIDI note) when the algorithm cannot find a fundamental at all. Each sample represents a value, which is every hopSize. Its sampling rate is sourceSR / hopSize. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the pitch descriptor to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be pitch-tracked. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed first. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be processed. - -ARGUMENT:: features - The destination buffer for the pitch descriptors. - -ARGUMENT:: algorithm - The algorithm to estimate the pitch. The options are: - TABLE:: - ## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC). - ## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 :: -## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications." QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html - :: - -ARGUMENT:: minFreq -The minimum frequency that the algorithm will search for an estimated fundamental. This sets the lowest value that will be generated. - -ARGUMENT:: maxFreq -The maximum frequency that the algorithm will search for an estimated fundamental. This sets the highest value that will be generated. - -ARGUMENT:: unit -The unit of the estimated value. The default of 0 is in Hz. A value of 1 will convert to MIDI note values. - -ARGUMENT:: windowSize - The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: padding - Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - -ARGUMENT:: freeWhenDone - Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// create a buffer with a short clicking sinusoidal burst (220Hz) starting at frame 8192 for 1024 frames -( -b = Buffer.sendCollection(s, (Array.fill(8192,{0}) ++ (Signal.sineFill(1203,[0,0,0,0,0,1],[0,0,0,0,0,0.5pi]).takeThese({|x,i|i>1023})) ++ Array.fill(8192,{0}))); -c = Buffer.new(s); -) - -// listen to the source and look at the buffer -b.play; b.plot; - -// run the process with basic parameters -( -Routine{ - t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the analysis -c.plot(separately:true) - -// The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopSize, then multiply by 2 because of the channel interleaving -// here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30. -c.getn(30,10,{|x|x.postln}) - -// observe that the first frame is silent, as expected. The next frame's confidence is low-ish, because the window is half full (window of 1024, overlap of 512). Then a full window is analysed, with strong confidence. Then another half full window, then silence, as expected. -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - -// create a buffer as destinations -c = Buffer.new(s); - -//run the process on them, with limited bandwidth and units in MIDI notes -( -Routine{ - t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the buffer: [pitch,confidence] for left then [pitch,confidence] for right -c.plot(separately:true) -:: - -STRONG::A musical example.:: -code:: -// create some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav")); -c = Buffer.new(s); -) - -// run the process with basic parameters and retrieve the array in the langage side -( -Routine{ - t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}).wait; - (Main.elapsedTime - t).postln; -}.play -) - -//look at the retrieved formatted array of [pitch,confidence] values -d.postln - -//iterate and make an array of the indices which are fitting the conditions -( -e = Array.new; -d.do({ - arg val, i; - if ((val[0] > 500) && (val[1] > 0.98)) {e = e.add(i)}; // if pitch is greater than 500Hz and confidence higher than 0.98, keep the index -}); -) -e.postln; - -//granulate only the frames that are in our buffer -// We need to convert our indices to frame start. Their position was (index * hopSize) - (hopSize) in FluidBufPitch -f = e.collect({arg i; (i * 512) - 512}); - -// define a basic grain synth -( -SynthDef(\grain, - { arg out=0, buf =0 , ind = 0, pan = 0; - var env; - env = EnvGen.kr(Env.new([0,1,0],[512/s.sampleRate].dup,\sine), doneAction: Done.freeSelf); - Out.ar(out, Pan2.ar(PlayBuf.ar(1,buf,startPos:ind),pan)); - }).add; -) - -// start the sequence -( -a = Pxrand(f, inf).asStream; -Routine({ - loop({ - Synth(\grain, [\buf, b, \ind, a.next, \pan, (2.0.rand - 1)]); - (256/s.sampleRate).wait; - }) -}).play; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufSTFT.schelp b/release-packaging/HelpSource/Classes/FluidBufSTFT.schelp deleted file mode 100644 index d0f3f3f..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufSTFT.schelp +++ /dev/null @@ -1,109 +0,0 @@ -TITLE:: FluidBufSTFT -summary:: Perform a Short-Time Fourier Transform on one channel of a buffer -categories:: Libraries>FluidCorpusManipulation -related:: Classes/Buffer - -DESCRIPTION:: -Performs either a forward or inverse Short-Time Fourier Transform (STFT) on a single channel source buffer~. In the forward case, resulting magnitudes and phases can be written to output buffers. In the inverse case, these buffers can be used to reconstruct the original source into a new buffer. - -The magntude and phase buffers are laid out as (number of hops, number of bins). The number of hops is a function of the source length and the hop size. The number of bins is (1 + (fft size / 2)). - -The object is restricted to analysing a single source channel, because the channel counts of the magntude and phase buffers would quickly get out of hand otherwise. - -CLASSMETHODS:: - -private::new1 - -METHOD:: process, processBlocking -Run the process on the given sever, and perfrom code::action:: when done - -ARGUMENT:: server -The link::Classes/Server:: on which to run - -ARGUMENT:: source -The link::Classes/Buffer:: to use for the forward STFT - -ARGUMENT:: startFrame -The starting point for analysis in the source (in samples) - -ARGUMENT:: numFrames -The duration (in samples) to analyse - -ARGUMENT:: startChan -The channel to analyse - -ARGUMENT:: magnitude -The link::Classes/Buffer:: to write magnitudes to in the forward case, or read from in the inverse case. This is optional for the forward transform, mandatory for the inverse. - -ARGUMENT:: phase -The link::Classes/Buffer:: to write phases to in the forward case, or read from in the inverse case. This is optional for the forward transform, mandatory for the inverse. - -ARGUMENT:: resynth -The link::Classes/Buffer:: to write re-synthesised data to in the inverse case. Ignored for the forward transform. Mandatory in the inverse case. - -ARGUMENT:: inverse -When set to 1, an inverse STFT is performed, and the resynthesised data is written to the resynthesis buffer using overlap-add. - -ARGUMENT:: windowSize -The number of source samples that are analysed at once. - -ARGUMENT:: hopSize -How many samples there are in-between analysis windows. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize -The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. For this object it is effectively capped at 65536. - -ARGUMENT:: padding -Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action -Runs when processing is complete - -INSTANCEMETHODS:: - - -EXAMPLES:: - -code:: -s.reboot -( -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -m = Buffer.new; -p = Buffer.new; -r = Buffer.new; -) - -( -fork{ - FluidBufSTFT.process(s,source:b,magnitude:m,phase:p).wait; - FluidBufSTFT.process(s,magnitude:m,phase:p,resynth:r,inverse:1).wait; - "Done".postln; -} -) - -{ PlayBuf.ar(1,r); }.play - -//nullsum -{ PlayBuf.ar(1,r) - PlayBuf(1,b); }.play - -//draw the magnitudes as a greyscale spectrogram -// make the image -i = Image.new(m.numFrames, m.numChannels) - -//retreive the image and assign to pixels -( -m.loadToFloatArray(action: {|x| - var mod = m.numChannels; - { - x.do{ - |val, index| - i.setColor(Color.gray(val), index.div(mod), mod - 1 - index.mod(mod)); - }; - i.plot("spectrogram", showInfo: false); - }.fork(AppClock) -}); -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufScale.schelp b/release-packaging/HelpSource/Classes/FluidBufScale.schelp deleted file mode 100644 index d511920..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufScale.schelp +++ /dev/null @@ -1,112 +0,0 @@ -TITLE:: FluidBufScale -SUMMARY:: A Scaling Processor for Buffers -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -This class implements a simple Buffer preprocessor, by scaling its values. It draws a simple translation from inputLow to outputLow, and from inputHigh to outputHigh. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a buffer with the same size and shape than the requested range. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the scaling to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffer to be processed is allocated. - -ARGUMENT:: source -The index of the buffer to use as the source material to be processed. - -ARGUMENT:: startFrame - The starting point (in samples) from which to copy in the source buffer. - -ARGUMENT:: numFrames - The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer. - -ARGUMENT:: startChan - The first channel from which to copy in the source buffer. - -ARGUMENT:: numChans - The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel. - -ARGUMENT:: destination -The index of the buffer to use as the destination for the processed material. - -ARGUMENT:: inputLow -The low reference point of the input. it will be scaled to yield outputLow at the output - -ARGUMENT:: inputHigh -The high reference point of the input. it will be scaled to yield outputHigh at the output - -ARGUMENT:: outputLow -The output value when the input is inputLow - -ARGUMENT:: outputHigh -The output value when the input is inputHigh - -ARGUMENT:: clipping -Optional clipping of the input (and therefore of the output). 0 is none. 1 caps the lowest input at inputLow. 2 caps the highest input at inputHigh, 3 caps both input low and high value within the described range. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. - -returns:: an instance of the processor - - -EXAMPLES:: - -code:: -( -Routine{ - // make a buffer of known qualities - b = Buffer.sendCollection(s,1.0.series(1.1,2.0)); - // and a destination buffer - c = Buffer(s); - // play with the scaling - FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10).wait; - // retrieve the buffer and enjoy the results. - c.getn(0,10,{|x|x.round(0.000001).postln;}) -}.play -) - -// also works in multichannel - explore the following buffer - -//process -( -Routine{ - b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2); - c = Buffer(s); - s.sync; - defer{b.plot(bounds:Rect(400,400,400,400)).plotMode_(\points).bounds}; - FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1).wait; - //enjoy - same shape, different range - defer{c.plot(bounds:Rect(800,400,400,400)).plotMode_(\points)}; -}.play; -) - -//also works with a subset of the input, resizing the output -( -Routine{ - b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3); - c = Buffer(s); - s.sync; - defer{b.plot(separately: true,bounds:Rect(400,400,400,400)).plotMode_(\points)}; - //process - FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1).wait; - //enjoy - c.query; - c.getn(0,4,{|x|x.postln;}); - defer{c.plot(separately: true,bounds:Rect(800,400,400,400)).plotMode_(\points)}; -}.play -) -:: - - diff --git a/release-packaging/HelpSource/Classes/FluidBufSelect.schelp b/release-packaging/HelpSource/Classes/FluidBufSelect.schelp deleted file mode 100644 index 55b2520..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufSelect.schelp +++ /dev/null @@ -1,62 +0,0 @@ -TITLE:: FluidBufSelect -summary:: Cherry pick values from a buffer -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidBufSelectEvery - -DESCRIPTION:: -Pick sets of values from a buffer, described in terms of a list of frame indices and channel numbers. - -CLASSMETHODS:: - -private::new1 - -METHOD:: process, processBlocking -Run the process on the given sever, and perfrom code::action:: when done - -ARGUMENT:: server -The link::Classes/Server:: on which to run - -ARGUMENT:: source -The link::Classes/Buffer:: to select values from - -ARGUMENT:: destination -The link::Classes/Buffer:: to write the selected data to - -ARGUMENT:: indices -A 0-based list of frame indices to recover. Default is [-1], meaning all frames - -ARGUMENT:: channels -A 0-based list of channel numbers to recover. Default is [-1], meaning all frames - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action -Runs when processing is complete - -EXAMPLES:: -code:: -//send a known collection where the value of each frame in each channel is encoded -//chan -b = Buffer.sendCollection(s,30.collect{|x| x.mod(6) + (x.div(6) * 0.1)},6) -//check the ranges (thus showing a plotter error...) -b.plot(separately: true).plotMode_(\points) -//you can also check the collection itself if in doubt -b.getToFloatArray(action: {|x|x.round(0.1).postln;}); - -//let's make a destination buffer -c = Buffer(s); - -//using default values, we copy everything: -FluidBufSelect.process(s,b,c,action: {c.query}); -c.getToFloatArray(action: {|x|x.round(0.1).postln;}); - -//more powerful copying, resizing the destination accordingly -FluidBufSelect.process(s,b,c, indices: [1,3], channels: [2,4], action: {c.query}); -c.getToFloatArray(action: {|x|x.round(0.1).postln;}); - -//observe the order can be anything, and -1 (default) passes everything in that dimension -FluidBufSelect.process(s,b,c, indices: [ -1 ] , channels: [3, 1, 4], action: {c.query}); -c.getToFloatArray(action: {|x|x.round(0.1).postln;}); -:: - diff --git a/release-packaging/HelpSource/Classes/FluidBufSelectEvery.schelp b/release-packaging/HelpSource/Classes/FluidBufSelectEvery.schelp deleted file mode 100644 index 6e20df2..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufSelectEvery.schelp +++ /dev/null @@ -1,74 +0,0 @@ -TITLE:: FluidBufSelectEvery -summary:: Extract every N samples / channels from a buffer -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidBufSelect - -DESCRIPTION:: -Pick every N frames and / or channels from a buffer, described in terms of independent hop sizes for frames and channels - -CLASSMETHODS:: - -private::new1 - -METHOD:: process, processBlocking -Run the process on the given sever, and perfrom code::action:: when done - -ARGUMENT:: server -The link::Classes/Server:: on which to run - -ARGUMENT:: source -The link::Classes/Buffer:: to select values from - -ARGUMENT:: startFrame -The starting point (in samples) from which to copy in the source buffer. - -ARGUMENT:: numFrames -The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer. - -ARGUMENT:: startChan -The first channel from which to copy in the source buffer. - -ARGUMENT:: numChans -The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel. - -ARGUMENT:: destination -The link::Classes/Buffer:: to write the selected data to - -ARGUMENT:: frameHop -Take every `frameHop` frames. Default = 1 = all frames (where 2 would be every other frame, etc.) - -ARGUMENT:: chanHop -Take every `chanHop` channels. Default = 1 = all channels (where 2 would be every other channel, etc.) - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action -Runs when processing is complete - -EXAMPLES:: -Didactic -code:: -//send a known collection where the value of each frame in each channel is encoded -//chan -b = Buffer.sendCollection(s,30.collect{|x| x.mod(6) + (x.div(6) * 0.1)},6) -//check the ranges (thus showing a plotter error...) -b.plot(separately: true).plotMode_(\points) -//you can also check the collection itself if in doubt -b.getToFloatArray(action: {|x|x.round(0.1).postln;}); - -//let's make a destination buffer -c = Buffer(s); - -//using default values, we copy everything: -FluidBufSelectEvery.process(s,b, destination: c, action: {c.query}); -c.getToFloatArray(action: {|x|x.round(0.1).postln;}); - -//more powerful copying, resizing the destination accordingly -FluidBufSelectEvery.process(s,b, destination: c, frameHop: 2, chanHop: 3, action: {c.query}); -c.getToFloatArray(action: {|x|x.round(0.1).postln;}); - -//source buffer boundaries still apply before the hopping selection -FluidBufSelectEvery.process(s,b, startFrame: 1, numFrames: 3, startChan: 2, numChans: 3, destination: c, frameHop: 1, chanHop: 2, action: {c.query}); -c.getToFloatArray(action: {|x|x.round(0.1).postln;}); -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufSines.schelp b/release-packaging/HelpSource/Classes/FluidBufSines.schelp deleted file mode 100644 index 0cdc5c4..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufSines.schelp +++ /dev/null @@ -1,148 +0,0 @@ -TITLE:: FluidBufSines -SUMMARY:: Buffer-Based Sinusoidal Modelling and Resynthesis -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Guides/FluidBufMultiThreading - - -DESCRIPTION:: -This class triggers a Sinusoidal Modelling process on buffers on the non-real-time thread of the server. It implements a mix of algorithms taken from classic papers. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - The algorithm will take a buffer in, and will divide it in two parts: LIST:: - ## a reconstruction of what it detects as sinusoidal; - ## a residual derived from the previous buffer to allow null-summing:: - - The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the sinusoidal estimation to be calculated on a given source buffer and to be resynthesised. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be decomposed through the sinusoidal modelling process. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed first. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be processed. - -ARGUMENT:: sines - The index of the buffer where the extracted sinusoidal component will be reconstructed. - -ARGUMENT:: residual - The index of the buffer where the residual of the sinusoidal component will be reconstructed. - -ARGUMENT:: bandwidth - The number of bins used to resynthesises a peak. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. It is capped at (fftSize / 2) + 1. - -ARGUMENT:: detectionThreshold - The threshold in dB above which a magnitude peak is considered to be a sinusoidal component. - -ARGUMENT:: birthLowThreshold - The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the low end of the spectrum. It is interpolated across the spectrum until birthHighThreshold at half-Nyquist. - -ARGUMENT:: birthHighThreshold - The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the high end of the spectrum. It is interpolated across the spectrum until birthLowThreshold at DC. - -ARGUMENT:: minTrackLen - The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefacts, but is more CPU intensive and might reject quick pitch material. - -ARGUMENT:: trackingMethod - The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive "Hungarian" one. footnote::Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018.:: - -ARGUMENT:: trackMagRange - The amplitude difference allowed for a track to diverge between frames, in dB. - -ARGUMENT:: trackFreqRange - The frequency difference allowed for a track to diverge between frames, in Hertz. - -ARGUMENT:: trackProb - The probability of the tracking algorithm to find a track. - -ARGUMENT:: windowSize - The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [sines, residual] as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// create some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); -c = Buffer.new(s); -d = Buffer.new(s); -) - -// run the process with basic parameters -( -Routine{ - t = Main.elapsedTime; - FluidBufSines.process(s, b, sines: c, residual:d).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// listen to each component -c.play; -d.play; - -//nullsumming tests -{(PlayBuf.ar(1, c)) + (PlayBuf.ar(1,d)) - (PlayBuf.ar(1,b,doneAction:2))}.play -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - -// create 2 new buffers as destinations -d = Buffer.new(s); e = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384).wait; - (Main.elapsedTime - t).postln; -}.play -) - -//listen: stereo preserved! -d.play -e.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp deleted file mode 100644 index 08e3cb8..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ /dev/null @@ -1,142 +0,0 @@ -TITLE:: FluidBufSpectralShape -SUMMARY:: Seven Spectral Shape Descriptors on a Buffer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidSpectralShape, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile - - -DESCRIPTION:: -This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The descriptors are: -LIST:: -##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: - LIST:: - ## the spectral centroid (1) in Hz. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in Hz. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below that frequency, i.e. the central bin of the magnitude spectrum, it is positive. - ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. - :: - ## the rolloff (5) in Hz. This indicates the frequency under which 95% of the energy is included. - ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. - ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve.:: - - The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. For examples of the impact of computing the moments in power magnitudes, and/or in exponential frequency scale, please refer to the LINK::Classes/FluidSpectralShape:: helpfile. - - The process will return a multichannel buffer with the seven channels per input channel, each containing the 7 shapes. Each sample represents a value, which is every hopSize. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed first. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be processed. - -ARGUMENT:: features - The destination buffer for the 7 spectral features describing the spectral shape. - -ARGUMENT:: minFreq - The minimum frequency that the algorithm will consider for computing the spectral shape. Frequencies below will be ignored. The default of 0 goes down to DC when possible. - -ARGUMENT:: maxFreq - The maximum frequency that the algorithm will consider for computing the spectral shape. Frequencies above will be ignored. The default of -1 goes up to Nyquist. - -ARGUMENT:: rolloffPercent - This sets the percentage of the frame's energy that will be reported as the rolloff frequency. The default is 95%. - -ARGUMENT:: unit - The frequency unit for the spectral shapes to be computed upon, and outputted at. The default (0) is in Hertz and computes the moments on a linear spectrum. The alternative is in MIDI note numbers(1), which compute the moments on an exponential spectrum. - -ARGUMENT:: power - This flag sets the scaling of the magnitudes in the moment calculation. It uses either its amplitude (0, by default) or its power (1). - -ARGUMENT:: windowSize - The window size. As spectral shape estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: padding -Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material. - -ARGUMENT:: freeWhenDone - Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// create some buffers -( -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -c = Buffer.new(s); -) - -// run the process with basic parameters -( -Routine{ - t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// listen to the source and look at the buffer -b.play; -c.plot(separately:true) -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - -// create a buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// look at the buffer: 7shapes for left, then 7 shapes for right -c.plot(separately:true) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp deleted file mode 100644 index 77cad30..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ /dev/null @@ -1,288 +0,0 @@ -TITLE:: FluidBufStats -SUMMARY:: Computing Statistics on Buffers as Series. -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - - -DESCRIPTION:: -This class implements non-real-time statistical analysis on buffer channels. Typically, a buffer would hold various time series (i.e. descriptors over time), and link::Classes/FluidBufStats:: allows this series to be described statistically. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process returns a buffer where each channel of the STRONG::source:: buffer has been reduced to 7 statistics: mean, standard deviation, skewness, kurtosis, followed by 3 percentiles, by default the minimum value, the median, and the maximum value. Moreover, it is possible to request the same 7 stats to be applied to derivative of the input. These are useful to describe statistically the rate of change of the time series. The STRONG::stats:: buffer will grow accordingly, yielding the seven same statistical description of the n requested derivatives. Therefore, the STRONG::stats:: buffer will have as many channel as the input buffer, and as many frames as 7 times the requested STRONG::numDerivs::. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking -This is the method that calls for the slicing to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be processed. The different channels of multichannel buffers will be considered independently as time series. - -ARGUMENT:: startFrame - The starting point (in samples) from which to copy in the source buffer. - -ARGUMENT:: numFrames - The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer. - -ARGUMENT:: startChan - The first channel from which to copy in the source buffer. - -ARGUMENT:: numChans - The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel. - -ARGUMENT:: stats - The index of the buffer to write the statistics to. Each channel is the fruit of the statistical computations on the same channel number of the source buffer. - -ARGUMENT:: numDerivs - The number of derivatives of the original time series for the statistic to be computed on. By default, none are computed. This will influence the number of frames the stats buffer will have. - -ARGUMENT:: low - The rank requested for the first percentile value. By default, it is percentile 0.0, which is the minimum of the given channel of the source buffer. - -ARGUMENT:: middle - The rank requested for the second percentile value. By default, it is percentile 50.0, which is the median of the given channel of the source buffer. - -ARGUMENT:: high - The rank requested for the third percentile value. By default, it is percentile 100.0, which is the maximum of the given channel of the source buffer. - -ARGUMENT:: outliersCutoff - A ratio of the inter quantile range (IQR) that defines a range outside of which data will be rejected. It is run on each channel independently and a single channel being flagged as outlier removes the whole frame (on all channels). The default (-1) bypasses this function, keeping all frames in the statistical measurements. For more information on this statistical process, please refer to the concept of IQR and how the whiskers of a box plot are computed here (https://en.wikipedia.org/wiki/Box_plot) - -ARGUMENT:: weights - A buffer to provide relative weighting of the source material. Not providing one will not apply weighting and consider all frames equally. The provided buffer has to satisfy all of the following conditions: LIST:: - ## a single-channel, that will be applied to all channels of source - ## exactly the same amount of frames as ‘source’ - ## weights must be positive (anything lower than 0 will be rejected) - :: - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed stats as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -STRONG::A didactic example:: - -CODE:: - -// make a buffer of known length -b = Buffer.alloc(s,101); - -// add known values - here, a ramp up -b.setn(0, Array.fill(101,{|i|i / 100})); - -// create a new buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufStats.process(s, b, stats:c, numDerivs:1).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// list the statistics. The first seven are for the source buffer values themselves, the last seven for the first derivative of the source buffer. -c.getn(0,c.numFrames,{|item|item.postln;}) - -// replace the source values by a ramp down -b.setn(0, Array.fill(101,{|i| 1 - (i / 100)})); - -// run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); - -// replace the source values by halfsine -b.setn(0, Array.fill(101,{|i| (i * pi/ 100).sin})); -b.plot - -// run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); - -// replace the source values by partial halfsine -b.setn(0, Array.fill(101,{|i| (i * pi/ 50).sin.max(0)})); -b.plot - -// run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); - -// replace the source values by positive white noise -b.setn(0, Array.fill(101,{1.0.rand})); -b.plot - -// run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); -:: - -STRONG::A musical example:: - -CODE:: -// create some buffers -( -// a simple random sliding bell synth -b = { - var trig = Impulse.ar(1.5); - SinOsc.ar( - Lag.ar(TRand.ar(trig: trig), - TRand.ar(0.5, trig: trig)).exprange(333,666), - mul: Decay.ar( - trig * TRand.ar(0.1,10,trig), - TRand.ar(0.5,1.1,trig) - ) - ).atan * 0.1; -}.asBuffer(20); -c = Buffer.new(s); -d = Buffer.new(s); -i = Buffer.new(s); -) - -//play the source -b.play; - -//split in various chunks, collecting the indices in an array -FluidBufOnsetSlice.process(s,b, threshold: 0.01, indices: c, action:{c.loadToFloatArray(action: {|array| e = array.add(b.numFrames);e.size.postln;e.postln;})}); - -//describe the whole input too, here using pitch, and collecting the values in an array, dismissing the (interleaved) confidence. -FluidBufPitch.process(s,b,features:d, windowSize: 4096, hopSize: 512, padding:2, action:{d.loadToFloatArray(action: {|array| f = array.unlace(2)[0]; f.postln;})}); - -// iterate through each slice, taking the median of the first derivative of the pitch of each -( -g= Array.new; -Routine({ - var nb = e.size; - e.doAdjacentPairs({ - arg start,end; - FluidBufStats.processBlocking(s,d,(start/512).asInteger,((end-start)/512).asInteger + 3,0,1,i,1, action: { - i.loadToFloatArray( action: { - arg array; - g = g.add(array[12]); - "% % %\n".postf((start/512).asInteger,((end-start)/512).asInteger + 3, array[12]);//adding some of the overlap but not more to not capture too much of the next attack - nb = nb - 1; - if (nb == 1, {"Done".postln;});//check if we've done all the pairs - }) - }).wait; - }); -}).play; -) - -//play in loop the slice in order of pitch direction (the median of the slice's pitch variation) - mouse on the left should be descending, in the middle should be more stable, and it should be ascending on the right. - -( -Buffer.sendCollection(s,g.order,action: {|x| { - var which = BufRd.kr(1, x, MouseX.kr(0, BufFrames.kr(x) - 1), 0, 1); - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(1,c,which,0,1), - BufRd.kr(1,c,which + 1,0,1), - BufRd.kr(1,c,which,0,1))); - }.play; - };) -) - -:: - - -STRONG::Stereo Input Behaviour:: - -CODE:: -// make a buffer of known lenght -b = Buffer.alloc(s,101,2); - -// add known values - here, a ramp up on the left and negative random values on the right -b.setn(0, Array.fill(101,{|i|[i / 100,-1.0.rand]}).flat); - -// plot to confirm -b.plot.plotMode_(\points) - -// create a new buffer as destinations -c = Buffer.new(s); - -// run the stats and send back the values -FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames * c.numChannels,{|item|d = item; d.postln})}); - -//looking at the result is not easy to grasp, since it is interleaved: first number is mean of L, second is mean of R, third is stddev of L, fourth is stddev or R -//this will make it tidier - the first value of each line is Left, the second is Right -d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t", "kurtosis\t\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln; -:: - -STRONG::Outliers and Weights:: - -CODE:: -// example 1a -// make a buffer of known qualities -b = Buffer.loadCollection(s,[1, 8, 9, 10, 11, 12, 99]); - -// plot to confirm -b.plot.plotMode = \points; - -// create a new buffer as destinations -c = Buffer.new(s); - -// run the stats and send back the values -FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln})}); -// run the same array with outliers rejected if outside of 1.5 times the IQR - observe the new minimum and maximum to see -FluidBufStats.process(s, b, stats:c, numDerivs:1, outliersCutoff: 1.5, action:{c.getn(0,c.numFrames,{|item| item.postln})}); - -// example 1b (run the stats above, and change the value of some elements in the array too) -b = Buffer.loadCollection(s,[1, 8, 9, 10, 11, 12, 16, 99].scramble); - -// example 1c (multichannel in behaviour is greedy) -// This mean that an outlier in any channel will dismiss the whole frame. -// For instance here the outlier is 99 (frame 8) in channel 0, and 1001 in channel 1 (frame 0) -// The final stats therefore has minima of [2,10002] and maxima of [8,10008] -e = [(1..8)++99, [1001] ++ 10002.series(10003,10009)].flop.scramble.flat -b = Buffer.loadCollection(s,e,2); -FluidBufStats.process(s, b, stats:c, numDerivs:1, outliersCutoff: 1.5, action:{c.getn(0,c.numFrames * c.numChannels,{|item| f =item.postln})}); - -//More readable format -f.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln; - -////////////// -// example 2a - -// make an array of 9 values, with known weigths. Scramble them pairwise for good measure (that should not change any stats) -e = [(1..9), 1.0.series(0.9,0.2)].flop.scramble.flop; -b = Buffer.loadCollection(s,e[0]); -c = Buffer.loadCollection(s,e[1]); -d = Buffer.new(s); -// run the stats and send back the values -FluidBufStats.process(s, b, stats:d, numDerivs:1, action:{d.getn(0,d.numFrames,{|item|item.postln})}); -// run the same array with the weights -FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: c, action:{d.getn(0,d.numFrames * d.numChannels,{|item|item.postln})}); - -// example 2b -e = [(1..9), 0.series(-10,-80)].flop.scramble.flop; -b = Buffer.loadCollection(s,e[0]); -c = Buffer.loadCollection(s,e[1]); -FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: c, action:{d.getn(0,d.numFrames * d.numChannels,{|item|item.postln})}); -// this has only negative weights, so it bails out, outputing all 0s. It also publishes a warning if the server options on verbosity are on. -// but if we scale them up -g = Buffer(s) -FluidBufScale.process(s,c,destination: g,inputLow: -100,inputHigh: 0) -// look at the new values - because 0->1 and -100->0 we get the same weights as example 2a -g.getn(0,9,{|x|x.postln}) -// run the stats - same results as example 2a -FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: g, action:{d.getn(0,d.numFrames * d.numChannels,{|item|item.postln})}); - -//example 2c (stereo input but mono weigths - works like a charm) -e = [(1..9), (101..109), 1.0.series(0.9,0.2)].flop.scramble.flop; -b = Buffer.loadCollection(s,e[0..1].flop.flat,2); -b.plot(separately: true).plotMode = \points; -c = Buffer.loadCollection(s,e[2]); -FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: c, action:{d.getn(0,d.numFrames * d.numChannels,{|item|f = item.postln})}); - -//More readable format -f.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln; - -//see the example folder for 2 musical comparisons: 1) weighted MFCCs providing different nearest neighbours, and 2) pitch manipulations -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp b/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp deleted file mode 100644 index 22667a4..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp +++ /dev/null @@ -1,71 +0,0 @@ -TITLE:: FluidBufThreadDemo -SUMMARY:: A Tutorial Object to Experiment with Multithreading in FluidBuf* Objects -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -DESCRIPTION:: -This class implements a simple tutorial object to illustrate and experiment with the behaviour of the FluidBuf* objects. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -It simply starts a process that will, upon completion of its task, write a single value to the destination buffer. This is the general behaviour of much of the CPU consuming FluidBuf* objects. In this case, as a toy example, the task is simply just wait and do nothing, to simulate a task that would actually take that long in the background. - -The process will, after waiting for STRONG::time:: millisecond, return its delay lenght as a Float writen at index [0] of the specified destination buffer. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the job to be done. In this case, simply waiting STRONG::time:: millisecond before writing a value in the destination buffer. - -ARGUMENT:: server - The server on which the destination buffer is declared. - -ARGUMENT:: result - The destination buffer, where the value should be written at the end of the process. - -ARGUMENT:: time - The duration in milliseconds of the delay that the background thread will wait for before yielding the value to the destination buffer. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A function that will be executed upon completion. It is passed the destination buffer as argument. - -returns::The instance of FluidNRTProcess which can be used to cancel the job. - - -METHOD:: kr - This is the UGen that is holding the link with the background thread. It is called by the 'process' method and can be used directly to monitor the progress of the job. For examples of such usage, please refer to the tutorial on link::Guides/FluidBufMultiThreading::. - -ARGUMENT:: result - The destination buffer, where the value should be written at the end of the process. - -ARGUMENT:: time - The duration of the delay that the background thread will wait for before yielding the value to the destination buffer. - -ARGUMENT:: trig - The trigger to start the job. - -ARGUMENT:: blocking - The thread on which the job is processed. - -returns::It report the approximate job progress, from 0 to 1. - - -EXAMPLES:: - -For a thorough explanation, please refer to the tutorial on link::Guides/FluidBufMultiThreading::. - -CODE:: -// define a destination buffer -b=Buffer.alloc(s,1); - -// a simple call, where we query the destination buffer upon completion with the action message. -FluidBufThreadDemo.process(s, b, 1000, action:{|x|x.get(0,{|y|y.postln});}); - -// as the 'process' returns its instance, we can cancel the process easily -c = FluidBufThreadDemo.process(s, b, 100000, action: {|x|x.get(0,{|y|y.postln});}); -c.cancel; - -// if a simple call to the UGen is used, the progress can be monitored. The usual cmd-period will cancel the job by freeing the synth. -{c = FluidBufThreadDemo.kr(b,10000).poll; FreeSelfWhenDone.kr(c)}.scope; -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufThresh.schelp b/release-packaging/HelpSource/Classes/FluidBufThresh.schelp deleted file mode 100644 index 6c9b057..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufThresh.schelp +++ /dev/null @@ -1,81 +0,0 @@ -TITLE:: FluidBufThresh -SUMMARY:: A Gate Processor for Buffers -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -DESCRIPTION:: -This class implements a simple Buffer preprocessor, by replacing values under a threshold by 0s. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a buffer with the same size and shape than the requested range. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the thresholding to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffer to be processed is allocated. - -ARGUMENT:: source -The index of the buffer to use as the source material to be processed. - -ARGUMENT:: startFrame - The starting point (in samples) from which to copy in the source buffer. - -ARGUMENT:: numFrames - The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer. - -ARGUMENT:: startChan - The first channel from which to copy in the source buffer. - -ARGUMENT:: numChans - The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel. - -ARGUMENT:: destination -The index of the buffer to use as the destination for the processed material. - -ARGUMENT:: threshold -The threshold under which values will be zeroed - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. - -returns:: an instance of the processor - -EXAMPLES:: - -code:: -// make a buffer of know qualities -b = Buffer.sendCollection(s,0.0.series(0.1,1.0)) -// and a destination buffer -c = Buffer(s) -// play with the threshold -FluidBufThresh.process(s, b, destination: c, threshold: 0.5) -// retrieve the buffer and enjoy the results. -c.getn(0,11,{|x|x.round(0.000001).postln;}) - -// also works in multichannel - explore the following buffer -b = Buffer.sendCollection(s,0.0.series(0.1,2.0).scramble,2) -b.plot.plotMode_(\points) -//process and keep just the top values -FluidBufThresh.process(s, b, destination: c, threshold: 1.6) -//enjoy -c.plot.plotMode_(\points) - -//also works with a subset of the input, resizing the output -b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3) -b.plot(separately: true).plotMode_(\points) -//process and keep just the top values -FluidBufThresh.process(s, b,startFrame: 3,numFrames: 4,startChan: 1,numChans: 1,destination: c, threshold: 1.6) -//enjoy -c.plot(separately: true).plotMode_(\points) -c.query -c.getn(0,4,{|x|x.round(0.000001).postln;}) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp deleted file mode 100644 index 57a6095..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp +++ /dev/null @@ -1,150 +0,0 @@ -TITLE:: FluidBufTransientSlice -SUMMARY:: Buffer-Based Transient-Based Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidBufTransients - -DESCRIPTION:: -This class implements a non-real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - The process will return a buffer which contains indices (in sample) of estimated starting points of the different slices. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking - This is the method that calls for the slicing to be calculated on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be sliced through transient identification. The different channels of multichannel buffers will be summed. - -ARGUMENT:: startFrame - Where in the srcBuf should the slicing process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be summed. - -ARGUMENT:: indices - The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. - -ARGUMENT:: order - The order in samples of the impulse response filter used to model the estimated continuous signal. It is how many previous samples are used by the algorithm to predict the next one as reference for the model. The higher the order, the more accurate is its spectral definition, not unlike fft, improving low frequency resolution, but it differs in that it is not conected to its temporal resolution. - -ARGUMENT:: blockSize - The size in samples of frame on which it the algorithm is operating. High values are more cpu intensive, and also determines the maximum transient size, which will not be allowed to be more than half that lenght in size. - -ARGUMENT:: padSize - The size of the handles on each sides of the block simply used for analysis purpose and avoid boundary issues. - -ARGUMENT:: skew - The nervousness of the bespoke detection function with values from -10 to 10. It allows to decide how peaks are amplified or smoothed before the thresholding. High values increase the sensitivity to small variations. - -ARGUMENT:: threshFwd - The threshold of the onset of the smoothed error function. It allows tight start of the identification of the anomaly as it proceeds forward. - -ARGUMENT:: threshBack - The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. - -ARGUMENT:: windowSize - The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positives. - -ARGUMENT:: clumpLength - The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. - -ARGUMENT:: minSliceLength - The minimum duration of a slice in samples. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument. - -returns:: an instance of the processor - - -EXAMPLES:: - -code:: -// load some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); -c = Buffer.new(s); -) - -// with basic parameters (wait for the computation time to appear) -( - Routine{ - t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, indices:c).wait; - (Main.elapsedTime - t).postln; -}.play -) - -//check the number of slices -c.query; - -//loops over a splice -( -{ - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(1, c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1), - BufRd.kr(1, c, - MouseX.kr(1, BufFrames.kr(c)), 0, 1), - BufRd.kr(1,c, - MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1); - }.play; -) - -// with everything changed to make it much better, at the cost of computation time (only 5 seconds are processed here, again wait for the (longer) computation time to appear) -( -Routine{ - t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, 0, 220500, 0, 1, c, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// play with the same player above to hear the segmentation difference -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// make a stereo buffer -b = Buffer.alloc(s,88200,2); - -// add some stereo clicks and listen to them -((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)}) - -b.play - -// create a new buffer as destinations -c = Buffer.new(s); - -//run the process on them -( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, indices: c, threshFwd: 1.2).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// list the indicies of detected attacks - the two input channels have been summed -c.getn(0,c.numFrames,{|item|(item*2).postln;}) -:: diff --git a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp deleted file mode 100644 index af113f7..0000000 --- a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp +++ /dev/null @@ -1,149 +0,0 @@ -TITLE:: FluidBufTransients -SUMMARY:: Buffer-Based Transient Extractor -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading - -DESCRIPTION:: -This class triggers a transient extractor on buffers on the non-real-time thread of the server. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - The algorithm will take a buffer in, and will divide it in two outputs: LIST:: - ## the transients, estimated from the signal and extracted from it; - ## the remainder of the material, as estimated by the algorithm.:: - - The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. - -STRONG::Threading:: - -By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::. - -CLASSMETHODS:: - -METHOD:: process, processBlocking -This is the method that calls for the transient extraction to be performed on a given source buffer. - -ARGUMENT:: server - The server on which the buffers to be processed are allocated. - -ARGUMENT:: source - The index of the buffer to use as the source material to be decomposed through the NMF process. The different channels of multichannel buffers will be processing sequentially. - -ARGUMENT:: startFrame - Where in the srcBuf should the NMF process start, in sample. - -ARGUMENT:: numFrames - How many frames should be processed. - -ARGUMENT:: startChan - For multichannel srcBuf, which channel should be processed first. - -ARGUMENT:: numChans - For multichannel srcBuf, how many channel should be processed. - -ARGUMENT:: transients - The index of the buffer where the extracted transient component will be reconstructed. - -ARGUMENT:: residual - The index of the buffer where the estimated continuous component will be reconstructed. - -ARGUMENT:: order - The order in samples of the impulse response filter used to model the estimated continuous signal. It is how many previous samples are used by the algorithm to predict the next one as reference for the model. The higher the order, the more accurate is its spectral definition, not unlike fft, improving low frequency resolution, but it differs in that it is not conected to its temporal resolution. - -ARGUMENT:: blockSize - The size in samples of frame on which it the algorithm is operating. High values are more cpu intensive, and also determines the maximum transient size, which will not be allowed to be more than half that lenght in size. - -ARGUMENT:: padSize - The size of the handles on each sides of the block simply used for analysis purpose and avoid boundary issues. - -ARGUMENT:: skew - The nervousness of the bespoke detection function with values from -10 to 10. It allows to decide how peaks are amplified or smoothed before the thresholding. High values increase the sensitivity to small variations. - -ARGUMENT:: threshFwd - The threshold of the onset of the smoothed error function. It allows tight start of the identification of the anomaly as it proceeds forward. - -ARGUMENT:: threshBack - The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. - -ARGUMENT:: windowSize - The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positive. - -ARGUMENT:: clumpLength - The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. - -ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true - -ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [transients, residual] as an argument. - -returns:: an instance of the processor - - -EXAMPLES:: - -code:: -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); -c = Buffer.new(s); -d = Buffer.new(s); -) - -// with basic params -( -Routine{ - t = Main.elapsedTime; - FluidBufTransients.process(s,b, transients:c, residual:d).wait; - (Main.elapsedTime - t).postln; -}.play -); - -// wait for the duration to appear in the post window as a cue that the computation is finished -c.play; -d.play; - -//nullsumming tests -{(PlayBuf.ar(1,c))+(PlayBuf.ar(1,d))+(-1*PlayBuf.ar(1,b,doneAction:2))}.play - - -// with everything changed to make it much better, at the cost of computation time (only 5 seconds are processed here) -( -Routine{ - t = Main.elapsedTime; - FluidBufTransients.process(s,b, 0, 220500, 0, 1, c, d, 200, 2048, 1024, 1, 3, 1, 15, 30).wait; - (Main.elapsedTime - t).postln; -}.play -) - -// wait for the duration to appear in the post window as a cue that the computation is finished -c.play; -d.play; -:: - -STRONG::A stereo buffer example.:: -CODE:: - -// load two very different files -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -) - -// composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000, destStartChan:1, destination:b) -b.play - -// create 2 new buffers as destinations -d = Buffer.new(s); e = Buffer.new(s); - -//run the process on them -( -Routine{ - t = Main.elapsedTime; - FluidBufTransients.process(s, b, transients: d, residual:e, threshFwd:1.2, clumpLength:40).wait; - (Main.elapsedTime - t).postln; -}.play -) - -//listen: stereo preserved! -d.play -e.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidChroma.schelp b/release-packaging/HelpSource/Classes/FluidChroma.schelp deleted file mode 100644 index 1d7b63f..0000000 --- a/release-packaging/HelpSource/Classes/FluidChroma.schelp +++ /dev/null @@ -1,227 +0,0 @@ -TITLE:: FluidChroma -SUMMARY:: A histogram of pitch classes in Real-Time -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Classes/FluidBufChroma,Classes/FluidPitch,Classes/FluidLoudness,Classes/FluidMFCC,Classes/FluidSpectralShape,Guides/FluidCorpusManipulationToolkit,Classes/FluidMFCC -DESCRIPTION:: - - - This class computes a histogram of the energy contained for each pitch class across the analysis frequency range. - - - - Also known as a chromagram, this typically allows you to get a contour of how much each semitone is represented in the spectrum over time. The number of chroma bins (and, thus, pitch classes) and the central reference frequency can be adjusted. - - The process will return a multichannel control steam of size maxNumChroma, which will be repeated if no change happens within the algorithm, i.e. when the hopSize is larger than the signal vector size. - - -CLASSMETHODS:: - -METHOD:: kr - -ARGUMENT:: in - - Audio-rate signal to analyze - -ARGUMENT:: numChroma - - - The number of chroma bins per octave. It will determine how many channels are output per input channel. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 2 - - ## - Maximum: CODE::maxNumChroma:: - - :: - -ARGUMENT:: ref - - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - ## - Maximum: 22000 - - :: - -ARGUMENT:: normalize - - - This flag enables the scaling of the output. It is off (0) by default. (1) will normalise each frame to sum to 1. (2) normalises each frame relative to the loudest chroma bin being 1. - - -ARGUMENT:: minFreq - - - The lower frequency included in the analysis, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: maxFreq - - - The highest frequency included in the analysis, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: -1 - - :: - -ARGUMENT:: windowSize - - - The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. - - - -ARGUMENT:: maxNumChroma - - - The maximum number of chroma bins. This sets the number of channels of the output stream, and therefore cannot be modulated. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 2 - - ## - Maximum: (max FFFT Size / 2) + 1`` (see maxFFTSize) - - :: - -ARGUMENT:: maxFFTSize - - - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - - - - -INSTANCEMETHODS:: - -EXAMPLES:: - -code:: -//create a monitoring bus for the descriptors -b = Bus.new(\control,0,24); - -//create a monitoring window for the values - -( -w = Window("Chroma Bins Monitor", Rect(10, 10, 620, 320)).front; -a = MultiSliderView(w,Rect(10, 10, 600, 300)).elasticMode_(1).isFilled_(1); -) - -//run the window updating routine. -( -~winRange = 0.1; -r = Routine { - { - b.get({ arg val; - { - if(w.isClosed.not) { - a.value = val/~winRange; - } - }.defer - }); - 0.01.wait; - }.loop -}.play -) - -//play a simple sound to observe the values -( -x = { - var source = SinOsc.ar(LFTri.kr(0.1).exprange(80,800),0,0.1); - Out.kr(b,FluidChroma.kr(source, numChroma: 24, maxNumChroma:24)); - source.dup; -}.play; -) - -//we can observe that in the low end, the precision of the fft is not good enough to give a sharp pitch centre. We still can observe the octaviation quantized in quarter tones. - -// free this source -x.free - -// load a more exciting one -c = Buffer.read(s,FluidFilesPath("Tremblay-SlideChoirAdd-M.wav")); - -// analyse with parameters to be changed -( -x = {arg chroma = 24, low = 20, high = 20000, norm=1, t_cue, sel=0; - var source = Select.ar(sel, [ - PlayBuf.ar(1,c,loop:1), - Saw.ar(TIRand.kr(60.dup(3),96,t_cue).poll(t_cue).midicps,0.05).sum; - ]); - Out.kr(b,FluidChroma.kr(source ,numChroma: chroma, minFreq: low, maxFreq: high, normalize: norm, maxNumChroma: 24, windowSize: 4096) / 10); - source.dup; -}.play; -) - -//set the winRange to a more informative value -~winRange = 0.03; - -//instead, let's normalise each frame independently -~winRange = 0.12; -x.set(\norm, 2); - -// observe the number of chroma. The unused ones at the top are not updated -x.set(\chroma,12) - -// back to the full range -x.set(\chroma,24) - -// change the source to random three-note chords -x.set(\sel, 1) - -// trigger new chords and observe the chroma contour -x.set(\t_cue, 1) - -// focus all the chroma bin on a low mid range (there might be nothing!) -x.set(\low,320, \high, 800) - -// or on a specific octave -x.set(\low, 60.midicps, \high, 72.midicps) - -// back to full range -x.set(\low,20, \high, 20000) - -// free everything -x.free;b.free;c.free;r.stop; -:: - -STRONG::A musical example:: - -CODE:: -//something will happen here. -:: diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp deleted file mode 100644 index 2adb0cd..0000000 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ /dev/null @@ -1,242 +0,0 @@ -TITLE:: FluidDataSet -summary:: Container that associates data points with identifiers -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidLabelSet, Classes/FluidKDTree, Classes/FluidKMeans -​ -DESCRIPTION:: -FluidDataSet is a container that associates data points with identifiers - -CLASSMETHODS:: -​ -PRIVATE:: asUGenInput - -METHOD:: new -Create a new instance of the DataSet, with the given name. If a DataSet with this name already exists, an exception will be thrown (see link::Classes/FluidDataSet#at:: to access an existing DataSet). - -ARGUMENT:: server -The link::Classes/Server:: on which to create the data set. - -​ -returns:: The new instance - -INSTANCEMETHODS:: -​ -PRIVATE:: init,id,cache - -METHOD:: addPoint -Add a new point to the FluidDataSet. The dimensionality of the FluidDataSet is governed by the size of the first point added. If the identifier already exists, or if the size of the data does not match the dimensionality of the FluidDataSet an error will be reported. -Will report an error if the identifier already exists, or if the size of the data does not match the dimensionality of the DataSet. -ARGUMENT:: identifier -The identifier for the point. -ARGUMENT:: buffer -A link::Classes/Buffer:: containing the data for the point. -ARGUMENT:: action -A function to run when the point has been added. -​​ -METHOD:: updatePoint -Update an existing identifier's data. If the identifier does not exist, or if the size of the data does not match the dimensionality of the FluidDataSet an error will be reported. -ARGUMENT:: identifier -The identifier for the point. -ARGUMENT:: buffer -A link::Classes/Buffer:: containing the data for the point. -ARGUMENT:: action -A function to run when the operation completes. - -METHOD:: getPoint -Retrieve a point from the data set into a link::Classes/Buffer::. If the identifier does not exist an error will be reported. -ARGUMENT:: identifier -The identifier for the point. -ARGUMENT:: buffer -A link::Classes/Buffer:: where the retrieved data will be stored. -ARGUMENT:: action -A function to run when the operation completes. - -METHOD:: deletePoint -Remove a point from the data set. If the identifier doesn't exist an error will be reported. ​ -​ARGUMENT:: identifier -The identifier to be deleted. -ARGUMENT:: action -A function to run when the operation completes. - -METHOD:: setPoint -Set the point. If the identifier exists, this method behaves like updatePoint. If the identifier doesn't exist, it behaves like addPoint. -ARGUMENT:: identifier -The identifier for the point. -ARGUMENT:: buffer -A link::Classes/Buffer:: containing the data for the point. -​​ARGUMENT:: action -A function to run when the operation completes. - -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 identifier if overwrite is set to 1. To add columns instead, see the 'transformJoin' method of link::Classes/FluidDataSetQuery:: - -METHOD:: free -Destroy the object on the server. - -METHOD:: print -Post an abbreviated content of the DataSet in the window by default, but you can supply a custom action instead. - -returns:: A link::Classes/Synth:: -​ -METHOD:: server -The server instance the object uses -​ -returns:: A link::Classes/Server:: - -EXAMPLES:: - -CODE:: -// Create a simple a one-dimensional data set, three ways -// Using routine -s.reboot; -( -fork{ - ~ds = FluidDataSet.new(s); - ~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; -}; -) - -//Using Dictionary -( -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; -} -) - -// Using a synth -( -~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, 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. -//create 2 datasets -( -~dsA = FluidDataSet.new(s); -~dsB = FluidDataSet.new(s); -) - -//feed them items with same dimensions but different identifiers -~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\one,1,\two,2])])); -~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,3,\four,4])])); -~dsA.print; -~dsB.print; - -// merge and check. it works. -~dsB.merge(~dsA) -~dsB.print; - -//change the content of the dataset to shared identifiers -~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,333,\four,444])])); -~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,3,\four,4])])); -~dsA.print; -~dsB.print; - -//try to merge, it does not update -~dsB.merge(~dsA) -~dsB.print; - -// add the overwrite flag, and it works -~dsB.merge(~dsA,1) -~dsB.print; -:: diff --git a/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp b/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp deleted file mode 100644 index 884d3cc..0000000 --- a/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp +++ /dev/null @@ -1,185 +0,0 @@ -TITLE:: FluidDataSetQuery -summary:: Query a FluidDataSet -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidDataSet - -DESCRIPTION:: -A selection of columns and a set of conditions that match rows of a FluidDataSet. -Use to filter and search in a database of descriptors. - -CLASSMETHODS:: - -METHOD:: new -Make a new instance -ARGUMENT:: server -The server on which to run this object - -INSTANCEMETHODS:: - -PRIVATE:: init - -METHOD:: addColumn -Add a column to the query -ARGUMENT:: column -Column index -ARGUMENT:: action -Run when done - -METHOD:: addRange -Add a range of columns to the query -ARGUMENT:: start -First index -ARGUMENT:: count -Number of columns -ARGUMENT:: action -Run when done - -METHOD:: filter -Filter rows according to some condition. - -Example: (3, ">", 0.5) filters rows where the value of the 4th column (starting at 0) is larger than 0.5. -ARGUMENT:: column -Column index -ARGUMENT:: condition -Condition string. Possible values: "==", "!=", "<", "<=", ">", ">=" -ARGUMENT:: value -Condition value -ARGUMENT:: action -Run when done - -METHOD:: and -Add a condition to an existing filter with an "and" connector. -ARGUMENT:: column -Column index -ARGUMENT:: condition -Condition string. Possible values: "==", "!=", "<", "<=", ">", ">=" -ARGUMENT:: value -Condition value -ARGUMENT:: action -Run when done - - -METHOD:: or -Add a condition to an existing filter with an "or" connector. -ARGUMENT:: column -Column index -ARGUMENT:: condition -Condition string. Possible values: "==", "!=", "<", "<=", ">", ">=" -ARGUMENT:: value -Condition value -ARGUMENT:: action -Run when done - -METHOD:: limit -Limit the number of resulting rows. -ARGUMENT:: rows -Maximum number of rows -ARGUMENT:: action -Run when done - -METHOD:: clear -Clear the query, remove all columns, filters and limit. -ARGUMENT:: action -Run when done - -METHOD:: transform -Apply the query to a source link::Classes/FluidDataSet:: and write to a destination. Can be the same. -ARGUMENT:: sourceDataSet -Source data, or the DataSet name -ARGUMENT:: destDataSet -Destination data, or the DataSet name -ARGUMENT:: action -Run when done - -METHOD:: transformJoin -Apply the query to a source link::Classes/FluidDataSet:: and join the resulting subset at the end of the items sharing the same identifiers in a second source. Items unique to a source dataset will be ignored. To add items at the end of a dataset instead, see the 'merge' method of link::Classes/FluidDataSet:: -ARGUMENT:: source1DataSet -Source data, or the DataSet name -ARGUMENT:: source2DataSet -Source data, or the DataSet name -ARGUMENT:: destDataSet -Destination data, or the DataSet name -ARGUMENT:: action -Run when done - - -EXAMPLES:: - -code:: - -s.reboot; -// Create a DataSet with known data -~dataSet= FluidDataSet(s); - -( -~points = 100.collect{|i|5.collect{|j|j+(i/100)}}; -~dataSet.clear; -~tmpbuf = Buffer.alloc(s,5); - -fork{ - s.sync; - ~points.do{|x,i| - ~tmpbuf.setn(0,x); - ~dataSet.addPoint(i,~tmpbuf); - s.sync; - } -} -) - -//check the source - the column is the integer part of the value, and the row is the fractional part. This will help us identify what we kept in our didactic query -~dataSet.print; - -// Prepare a FluidDataSetQuery object -~query = FluidDataSetQuery.new; -~out = FluidDataSet(s); - -// prepare a simple query -~query.filter(0,"<",0.04); -~query.addColumn(2); -~query.transform(~dataSet, ~out); - -// check the result -~out.print; - -//prepare a more complex query -~query.clear; -~query.filter(0,">",0.03); -~query.and(1,"<",1.08); -~query.or(2,">",2.98); -~query.addRange(2,2); -~query.transform(~dataSet, ~out); - -// Check the results -~out.print; - -:: - -STRONG:: Joining Datasets:: - -code:: -//this is how to join 2 datasets, adding columns to items with the same identifier -//create 3 datasets -( -~dsA = FluidDataSet(s); -~dsB = FluidDataSet(s); -~dsC = FluidDataSet(s); -) - -//feed them items with almost overlaping identifier lists but with different dimensions -~dsA.load(Dictionary.newFrom([\cols, 2, \data, Dictionary.newFrom([\zero, [0,0], \one,[1,11],\two,[2,22], \three,[3,33],\four,[4,44]])])); -~dsB.load(Dictionary.newFrom([\cols, 2, \data, Dictionary.newFrom([\one,[111,1111],\two,[222,2222], \three,[333,3333],\four,[444,4444],\five,[555,5555]])])); -~dsA.print; -~dsB.print; - -// no query/filter defined, copies all items with identifiers common to both, and all of the defined column of the first input -~joiner = FluidDataSetQuery.new; -~joiner.transformJoin(~dsA,~dsB,~dsC) -~dsC.print - -// all the sophisticated conditions applicable to 'transform' can be done on the first dataset too. Selected columns of the first source are appended to matching items in the second source. -~joiner.filter(0,">",2.1) -~joiner.and(1,"<", 40) -~joiner.addColumn(0) -~joiner.transformJoin(~dsA,~dsB,~dsC) -~dsC.print -:: diff --git a/release-packaging/HelpSource/Classes/FluidGain.schelp b/release-packaging/HelpSource/Classes/FluidGain.schelp deleted file mode 100644 index 30775f1..0000000 --- a/release-packaging/HelpSource/Classes/FluidGain.schelp +++ /dev/null @@ -1,38 +0,0 @@ -TITLE:: FluidGain -SUMMARY:: Real-Time Buffered Gain Changer -CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Algebraic,UGens>Buffer -RELATED:: Guides/FluidCorpusManipulation,Classes/UnaryOpFunction - - -DESCRIPTION:: -This class implements a sanity test for the FluCoMa Real-Time Client Wrapper. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The input to be processed - -ARGUMENT:: gain - Audio or control rate change of the gain. - -RETURNS:: - Same as input, multiplied by the gain factor. - - -EXAMPLES:: - Summing with the inverse (gain of -1) gives us CPU-expensive silence. -CODE:: -{ var source = PinkNoise.ar(0.1); source + FluidGain.ar(source,-1); }.play -:: -Varying the gain at audio rate. -CODE:: -{ FluidGain.ar(PinkNoise.ar(0.1), gain:LFTri.ar(1)) }.play -:: -Varying the gain at control rate, in beautiful stereo. -CODE:: -{ FluidGain.ar(SinOsc.ar([222,333],mul:0.1), gain:LFTri.kr([0.5,0.7])) }.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidGrid.schelp b/release-packaging/HelpSource/Classes/FluidGrid.schelp deleted file mode 100644 index 3b910d7..0000000 --- a/release-packaging/HelpSource/Classes/FluidGrid.schelp +++ /dev/null @@ -1,201 +0,0 @@ -TITLE:: FluidGrid -summary:: Constrain a 2D DataSet into a Grid. -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidMDS, Classes/FluidPCA, Classes/FluidUMAP, Classes/FluidDataSet - -DESCRIPTION:: - -Maps a set of 2D points in a link::Classes/FluidDataSet:: to a rectangular grid using a variant of the link::https://www.gwern.net/docs/statistics/decision/1987-jonker.pdf##Jonker-Volgenant algorithm::. It can be useful to obtain compact grid layouts from the output of dimensionality reduction algorithms such as link::Classes/FluidUMAP::, link::Classes/FluidPCA:: or link::Classes/FluidMDS::. - -This is similar to projects like CloudToGrid (https://github.com/kylemcdonald/CloudToGrid/), RasterFairy (https://github.com/Quasimondo/RasterFairy) or IsoMatch (https://github.com/ohadf/isomatch). - - -CLASSMETHODS:: - -METHOD:: new -Make a new instance - -ARGUMENT:: server -The server on which to run this model - -ARGUMENT:: oversample -A factor to oversample the destination grid. The default is 1, so the grid has the same number of points as the input. Factors of 2 or more will allow a larger destination grid, which will respect the original shape a little more, but will also be sparser. - -ARGUMENT:: extent -The size to which the selected link::#axis:: will be constrained. The default is code::0::, which turns the constraints off. - -ARGUMENT:: axis -The axis to which the link::#extent:: constraint is applied. The default (code::0::) is horizontal, and (code::1::) is vertical. - -INSTANCEMETHODS:: - -PRIVATE:: init - -METHOD:: fitTransform -Fit the model to a link::Classes/FluidDataSet:: and write the new projected data to a destination FluidDataSet. -ARGUMENT:: sourceDataSet -Source FluidDataSet instance -ARGUMENT:: destDataSet -Destination FluidDataSet instance -ARGUMENT:: action -Run when done - -EXAMPLES:: - -STRONG::A didactic example:: - -code:: - -/// make a simple grid of numbers -~simple = Dictionary.newFrom(36.collect{|i|[i.asSymbol, [i.mod(9), i.div(9)]]}.flatten(1)); - -//look at it -( -w = Window("the source", Rect(128, 64, 230, 100)); -w.drawFunc = { - Pen.use { - ~simple.keysValuesDo{|key, val| - Pen.stringCenteredIn(key, Rect((val[0] * 25), (val[1] * 25), 25, 25), Font( "Helvetica", 12 ), Color.black) - } - } -}; -w.refresh; -w.front; -) - - -//load it in a dataset -~raw = FluidDataSet(s); -~raw.load(Dictionary.newFrom([\cols, 2, \data, ~simple])); - -// make a grid out of it -~grid = FluidGrid(s); -~gridified = FluidDataSet(s); -~grid.fitTransform(~raw, ~gridified, action:{~gridified.dump{|x| ~gridifiedDict = x["data"]; \gridded.postln;}}) - -// watch the grid -( -w = Window("a perspective", Rect(358, 64, 350, 230)); -w.drawFunc = { - Pen.use { - ~gridifiedDict.keysValuesDo{|key, val| - Pen.stringCenteredIn(key, Rect((val[0] * 25), (val[1] * 25), 25, 25), Font( "Helvetica", 12 ), Color.black) - } - } -}; -w.refresh; -w.front; -) - -// change the constraints and draw again -( -~grid.axis_(0).extent_(4).fitTransform(~raw, ~gridified, action:{ - ~gridified.dump{|x| - ~gridifiedDict = x["data"];\gridded.postln; - {w.refresh;}.defer; -}}) -) - -// here we constrain in the other dimension -( -~grid.axis_(1).extent_(3).fitTransform(~raw, ~gridified, action:{ - ~gridified.dump{|x| - ~gridifiedDict = x["data"];\gridded.postln; - {w.refresh;}.defer; -}}) -) - -:: - -STRONG::A more colourful example exploring oversampling:: - -code:: - -// make all dependencies -( -~raw = FluidDataSet(s); -~standardized = FluidDataSet(s); -~reduced = FluidDataSet(s); -~normalized = FluidDataSet(s); -~standardizer = FluidStandardize(s); -~normalizer = FluidNormalize(s, 0.05, 0.95); -~umap = FluidUMAP(s).numDimensions_(2).numNeighbours_(5).minDist_(0.2).iterations_(50).learnRate_(0.2); -~grid = FluidGrid(s); -~gridified = FluidDataSet(s); -) - -// build a dataset of 400 points in 3D (colour in RGB) -~colours = Dictionary.newFrom(400.collect{|i|[("entry"++i).asSymbol, 3.collect{1.0.rand}]}.flatten(1)); -~raw.load(Dictionary.newFrom([\cols, 3, \data, ~colours])); - -//First standardize our DataSet, then apply the UMAP to get 2 dimensions, then normalise these 2 for drawing in the full window size -( -~standardizer.fitTransform(~raw,~standardized,action:{"Standardized".postln}); -~umap.fitTransform(~standardized,~reduced,action:{"Finished UMAP".postln}); -~normalizer.fitTransform(~reduced,~normalized,action:{"Normalized Output".postln}); -) -//we recover the reduced dataset -~normalized.dump{|x| ~normalizedDict = x["data"]}; - -//Visualise the 2D projection of our original 4D data -( -w = Window("a perspective", Rect(128, 64, 200, 200)); -w.drawFunc = { - Pen.use { - ~normalizedDict.keysValuesDo{|key, val| - Pen.fillColor = Color.new(~colours[key.asSymbol][0], ~colours[key.asSymbol][1],~colours[key.asSymbol][2]); - Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); - ~colours[key.asSymbol].flat; - } - } -}; -w.refresh; -w.front; -) - -//Force the UMAP-reduced dataset into a grid, normalise for viewing then print in another window -( -~grid.fitTransform(~reduced,~gridified,action:{"Gridded Output".postln; - ~normalizer.fitTransform(~gridified,~normalized,action:{"Normalized Output".postln; - ~normalized.dump{|x| - ~normalizedDict = x["data"]; - { - y = Window("a grid", Rect(328, 64, 200, 200)); - y.drawFunc = { - Pen.use { - ~normalizedDict.keysValuesDo{|key, val| - Pen.fillColor = Color.new(~colours[key.asSymbol][0], ~colours[key.asSymbol][1],~colours[key.asSymbol][2]); - Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); - ~colours[key.asSymbol].flat; - } - } - }; - y.refresh; - y.front; - }.defer; - }; - }); -}); -) - -// We can check the dimensions of the yielded grid by dumping the normalisation.The grid coordinates are zero-counting -~normalizer.dump{|x|x["data_max"].postln} - -// This looks ok, but let's improve it with oversampling -( -~grid.oversample_(3).fitTransform(~reduced,~gridified,action:{"Gridded Output".postln; - ~normalizer.fitTransform(~gridified,~normalized,action:{"Normalized Output".postln; - ~normalized.dump{|x| - ~normalizedDict = x["data"]; - { - y.refresh; - }.defer; - }; - }); -}); -) - -// Again, checking the normalisation dump to check the maxima of each dimension -~normalizer.dump{|x|x["data_max"].postln} - -:: diff --git a/release-packaging/HelpSource/Classes/FluidHPSS.schelp b/release-packaging/HelpSource/Classes/FluidHPSS.schelp deleted file mode 100644 index 3562b0a..0000000 --- a/release-packaging/HelpSource/Classes/FluidHPSS.schelp +++ /dev/null @@ -1,114 +0,0 @@ -TITLE:: FluidHPSS -SUMMARY:: Harmonic-Percussive Source Separation Using Median Filtering -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -A FluidHPSS object performs Harmonic-Percussive Source Separation (HPSS) on the an audio input. The class performs HPSS as described in its original form footnote:: -Fitzgerald, Derry. 2010. ‘Harmonic/Percussive Separation Using Median Filtering’. In Proceedings DaFx 10. https://arrow.dit.ie/argcon/67. -:: as well as a variation on the extension propsoed by Driedger et al. footnote:: -Driedger, Jonathan, Meinard Uller, and Sascha Disch. 2014. ‘Extending Harmonic-Percussive Separation of Audio Signals’. In Proc. ISMIR. http://www.terasoft.com.tw/conf/ismir2014/proceedings/T110_127_Paper.pdf. -:: - -The algorithm takes an audio in, and divides it into two or three outputs, depending on the mode: LIST:: - ## an harmonic component; - ## a percussive component; - ## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.:: - -It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The input to be processed. - -ARGUMENT:: harmFilterSize - The size, in spectral frames, of the median filter for the harmonic component. Must be an odd number, >= 3. - -ARGUMENT:: percFilterSize - The size, in spectral bins, of the median filter for the percussive component. Must be an odd number, >=3 - -ARGUMENT:: maskingMode - The way the masking is applied to the original spectrogram. (0,1,2) - table:: - ## 0 || The traditional soft mask used in Fitzgerald's original method of 'Wiener-inspired' filtering. Complimentary, soft masks are made for the harmonic and percussive parts by allocating some fraction of a point in time-frequency to each. This provides the fewest artefacts, but the weakest separation. The two resulting buffers will sum to exactly the original material. - ## 1 || Relative mode - Better separation, with more artefacts. The harmonic mask is constructed using a binary decision, based on whether a threshold is exceeded at a given time-frequency point (these are set using harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, see below). The percussive mask is then formed as the inverse of the harmonic one, meaning that as above, the two components will sum to the original sound. - - ## 2 || Inter-dependent mode - Thresholds can be varied independently, but are coupled in effect. Binary masks are made for each of the harmonic and percussive components, and the masks are converted to soft at the end so that everything null sums even if the params are independent, that is what makes it harder to control. These aren't guranteed to cover the whole sound; in this case the 'leftovers' will placed into a third buffer. - :: - -ARGUMENT:: harmThreshFreq1 - In modes 1 and 2, the frequency of the low part of the threshold for the harmonic filter (0-1) - -ARGUMENT:: harmThreshAmp1 - In modes 1 and 2, the threshold of the low part for the harmonic filter. That threshold applies to all frequencies up to harmThreshFreq1: how much more powerful (in dB) the harmonic median filter needs to be than the percussive median filter for this bin to be counted as harmonic. - -ARGUMENT:: harmThreshFreq2 - In modes 1 and 2, the frequency of the hight part of the threshold for the harmonic filter. (0-1) - -ARGUMENT:: harmThreshAmp2 - In modes 1 and 2, the threshold of the high part for the harmonic filter. That threshold applies to all frequencies above harmThreshFreq2. The threshold between harmThreshFreq1 and harmThreshFreq2 is interpolated between harmThreshAmp1 and harmThreshAmp2. How much more powerful (in dB) the harmonic median filter needs to be than the percussive median filter for this bin to be counted as harmonic. - -ARGUMENT:: percThreshFreq1 - In mode 2, the frequency of the low part of the threshold for the percussive filter. (0-1) - -ARGUMENT:: percThreshAmp1 - In mode 2, the threshold of the low part for the percussive filter. That threshold applies to all frequencies up to percThreshFreq1. How much more powerful (in dB) the percussive median filter needs to be than the harmonic median filter for this bin to be counted as percussive. - -ARGUMENT:: percThreshFreq2 - In mode 2, the frequency of the hight part of the threshold for the percussive filter. (0-1) - -ARGUMENT:: percThreshAmp2 - In mode 2, the threshold of the high part for the percussive filter. That threshold applies to all frequencies above percThreshFreq2. The threshold between percThreshFreq1 and percThreshFreq2 is interpolated between percThreshAmp1 and percThreshAmp2. How much more powerful (in dB) the percussive median filter needs to be than the harmonic median filter for this bin to be counted as percussive. - -ARGUMENT:: windowSize - The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -ARGUMENT::maxHarmFilterSize - How large can the harmonic filter be modulated to (harmFilterSize), by allocating memory at instantiation time. This cannot be modulated. - -ARGUMENT:: maxPercFilterSize - How large can the percussive filter be modulated to (percFilterSize), by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - An array of three audio streams: [0] is the harmonic part extracted, [1] is the percussive part extracted, [2] is the rest. The latency between the input and the output is ((harmFilterSize - 1) * hopSize) + windowSize) samples. - -Discussion:: - HPSS works by using median filters on the spectral magnitudes of a sound. It hinges on a simple modelling assumption that tonal components will tend to yield concentrations of energy across time, spread out in frequency, and percussive components will manifest as concentrations of energy across frequency, spread out in time. By using median filters across time and frequency respectively, we get initial esitmates of the tonal-ness / transient-ness of a point in time and frequency. These are then combined into 'masks' that are applied to the orginal spectral data in order to produce a separation. - - The maskingMode parameter provides different approaches to combinging estimates and producing masks. Some settings (especially in modes 1 & 2) will provide better separation but with more artefacts. These can, in principle, be ameliorated by applying smoothing filters to the masks before transforming back to the time-domain (not yet implemented). - -EXAMPLES:: - -CODE:: -//load a soundfile to play -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); - -// run with basic parameters (left is harmonic, right is percussive) -{FluidHPSS.ar(PlayBuf.ar(1,b,loop:1))}.play - -// run in mode 1 -{FluidHPSS.ar(PlayBuf.ar(1,b,loop:1),17,51,1,0.05,40,0.1,-40)}.play - -// run in mode 2, listening to: -//the harmonic stream -{FluidHPSS.ar(PlayBuf.ar(1,b,loop:1),15,31,2,0.05,40,0.1,-40, 0.1, -10, 0.2, 10)[0].dup}.play -// the percussive stream -{FluidHPSS.ar(PlayBuf.ar(1,b,loop:1),15,31,2,0.05,40,0.1,-40, 0.1, -10, 0.2, 10)[1].dup}.play -// the residual stream -{FluidHPSS.ar(PlayBuf.ar(1,b,loop:1),15,31,2,0.05,40,0.1,-40, 0.1, -10, 0.2, 10)[2].dup}.play - -// null test (the process add a latency of ((harmFilterSize - 1) * hopSize) + windowSize) samples - {var sig = PlayBuf.ar(1,b,loop:1); [FluidHPSS.ar(sig, 17, 31).sum - DelayN.ar(sig, 1, ((((17 - 1) * 512) + 1024) / s.sampleRate))]}.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp deleted file mode 100644 index ae60eb8..0000000 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ /dev/null @@ -1,177 +0,0 @@ -TITLE:: FluidKDTree -summary:: KD Tree on the server -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidDataSet - -DESCRIPTION:: -A server-side K-Dimensional tree for efficient neighbourhood searches of multi-dimensional data. - -See https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbor-algorithms for more on KD Trees - -CLASSMETHODS:: - -METHOD:: new -Make a new KDTree model for the given server. -ARGUMENT:: server -The server on which to make the model. -ARGUMENT:: numNeighbours -The number of neighbours to return. A 0 will return all points in order of distance. When a radius is defined, numNeighbours is the maximum of items returned. -ARGUMENT:: radius -The threshold of acceptable distance for a point to be returned. A 0 will bypass this function, returning numNeighbours points. -ARGUMENT:: lookupDataSet -An optional link::Classes/FluidDataSet:: from which data points will be returned for realtime queries. This does not need to be the same DataSet that the tree was fitted against, but does need to have matching labels. Using this mechanism, we have a way to, e.g. associate labels with segments of playback buffers, without needing pass strings around the server. warning::This parameter can not be changed after the instance of FluidKDTree has been created:: - -INSTANCEMETHODS:: - -METHOD:: fit -Build the tree by scanning the points of a LINK::Classes/FluidDataSet:: -ARGUMENT:: dataSet -The LINK::Classes/FluidDataSet:: of interest. This can either be a data set object itself, or the name of one. -ARGUMENT:: action -A function to run when indexing is complete. - -METHOD:: kNearest -Returns the IDs of the CODE::k:: points nearest to the one passed. -ARGUMENT:: buffer -A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. -ARGUMENT:: action -A function that will run when the query returns, whose argument is an array of point IDs from the tree's LINK::Classes/FluidDataSet:: - -METHOD:: kNearestDist -Get the distances of the K nearest neighbours to a point. -ARGUMENT:: buffer -A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. -ARGUMENT:: action -A function that will run when the query returns, whose argument is an array of distances. - - -EXAMPLES:: - -code:: -// Make a DataSet of random 2D points -s.reboot; -( -fork{ - d = Dictionary.with( - *[\cols -> 2,\data -> Dictionary.newFrom( - 100.collect{|i| [i, [ 1.0.linrand,1.0.linrand]]}.flatten)]); - ~ds = FluidDataSet(s); - ~ds.load(d, {~ds.print}); -} -) - -// Make a new tree, and fit it to the DataSet -~tree = FluidKDTree(s,numNeighbours:5); - -//Fit it to the DataSet -~tree.fit(~ds); - -// Should be 100 points, 2 columns -~tree.size; -~tree.cols; - -//Return the labels of k nearest points to a new point -( -~p = [ 1.0.linrand,1.0.linrand ]; -~tmpbuf = Buffer.loadCollection(s, ~p, 1, { - ~tree.kNearest(~tmpbuf,{ |a|a.postln;~nearest = a;}) -}); -) - -// Retrieve values from the DataSet by iterating through the returned labels -( -fork{ - ~nearest.do{|n| - ~ds.getPoint(n, ~tmpbuf, {~tmpbuf.getn(0, 2, {|x|x.postln})}); - s.sync; - } -} -) -// Distances of the nearest points -~tree.kNearestDist(~tmpbuf, { |a| a.postln }); - -// Explore changing the number of neighbourgs -~tree.numNeighbours = 11; // note that this value needs to be sent to the server -~tree.kNearest(~tmpbuf,{ |a|a.postln;}); -~tree.numNeighbours = 0; // 0 will return all items in order of distance -~tree.kNearest(~tmpbuf,{ |a|a.postln;}); - -// Limit the search to an acceptable distance in a radius -// Define a point, and observe typical distance values -~p = [ 0.4,0.4]; -( -~tmpbuf = Buffer.loadCollection(s, ~p, 1, { - ~tree.kNearest(~tmpbuf,{ |a|a.postln;}); - ~tree.kNearestDist(~tmpbuf,{ |a|a.postln;}); -}); -) - -// enter a valid radius. -~tree.radius = 0.1; -// FluidKDTree will return only values that are within that radius, up to numNeighbours values -( -~tmpbuf = Buffer.loadCollection(s, ~p, 1, { - ~tree.kNearest(~tmpbuf,{ |a|a.postln;}); -}); -) -:: - -subsection:: Queries in a Synth - -Input and output is done via buffers, which will need to be preallocated to the correct sizes: -LIST:: -##Your input buffer should be sized to the input data dimension (2, in this example) -##Your output buffer should be the number of neighbours * output dimensionality -:: - -We can't simply return labels (i.e. strings) from a UGen, so the query returns the actual data points from a DataSet instead. By default, this is the FluidDataSet against which the tree was fitted. However, by passing a different dataset to code::kr::'s code::lookupDataSet:: argument instead, you can return different points, so long as the labels in the two datasets match. In this way, the FluidKDTree can be used to perform nearest neighbour mappings in a synth. - -For instance, whilst fitting the tree against some n-dimensional descriptor data, our lookup dataset could use the same labels to map descriptor entries back to buffers, or locations in buffers, so that queries can be used to trigger audio. - -code:: - -( -Routine{ - var inputBuffer = Buffer.alloc(s,2); - var outputBuffer = Buffer.alloc(s,10);//5 neighbours * 2D data points - s.sync; - { - var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 - var point = 2.collect{TRand.kr(0,1,trig)}; - point.collect{|p,i| BufWr.kr([p],inputBuffer,i)}; - ~tree.kr(trig,inputBuffer,outputBuffer,5,nil); - Poll.kr(trig, BufRd.kr(1,outputBuffer,Array.iota(10)),10.collect{|i| "Neighbour" + (i/2).asInteger ++ "-" ++ (i.mod(2))}); - Silent.ar; - }.play; -}.play; -) - -//Using a lookup data set instead: -//here we populate with numbers that are in effect the indicies, but it could be anything numerical that will be returned on the server-side and would be usable on that side -( -fork{ - d = Dictionary.with( - *[\cols -> 1,\data -> Dictionary.newFrom( - 100.collect{|i| [i, [ i ]]}.flatten)]); - ~dsL = FluidDataSet.new(s); - ~dsL.load(d, {~dsL.print}); -} -) - -// Create the buffers, and make a synth, querying our tree with some random points -( -Routine{ - var inputBuffer = Buffer.alloc(s,2); - var outputBuffer = Buffer.alloc(s,5);//5 neighbours * 1D points - s.sync; - { - var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 - var point = 2.collect{TRand.kr(0,1,trig)}; - point.collect{|p,i| BufWr.kr([p],inputBuffer,i)}; - ~tree.kr(trig,inputBuffer,outputBuffer,5,~dsL); - Poll.kr(trig, BufRd.kr(1,outputBuffer,Array.iota(5)),5.collect{|i| "Neighbour" + i}); - Silent.ar; - }.play; -}.play; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp deleted file mode 100644 index 97db48a..0000000 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ /dev/null @@ -1,259 +0,0 @@ -TITLE:: FluidKMeans -summary:: Cluster data points with K-Means -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidDataSet, Classes/FluidLabelSet, Classes/FluidKNNClassifier, Classes/FluidKNNRegressor - -DESCRIPTION:: -Uses the K-Means algorithm to learn clusters from a link::Classes/FluidDataSet:: - -https://scikit-learn.org/stable/tutorial/statistical_inference/unsupervised_learning.html#clustering-grouping-observations-together - -CLASSMETHODS:: - -METHOD:: new -Construct a new K Means model on the passed server. The parameters code::numClusters:: and code::maxIter:: can be modulated on an existing instance. -ARGUMENT:: server -If nil will use Server.default. -ARGUMENT:: numClusters -The number of clusters to classify data into. -ARGUMENT:: maxIter -The maximum number of iterations the algorithm will use whilst fitting. - - -INSTANCEMETHODS:: - -PRIVATE::k - -METHOD:: fit -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 link::Classes/FluidDataSet:: to a link::Classes/FluidLabelSet::. -ARGUMENT:: dataSet -A link::Classes/FluidDataSet:: containing the data to predict. -ARGUMENT:: labelSet -A link::Classes/FluidLabelSet:: to retrieve the predicted clusters. -ARGUMENT:: action -A function to run when the server responds. - -METHOD:: fitPredict -Run link::Classes/FluidKMeans#*fit:: and link::Classes/FluidKMeans#*predict:: in a single pass: i.e. train the model on the incoming link::Classes/FluidDataSet:: and then return the learned clustering to the passed link::Classes/FluidLabelSet:: -ARGUMENT:: dataSet -a link::Classes/FluidDataSet:: containing the data to fit and predict. -ARGUMENT:: labelSet -a link::Classes/FluidLabelSet:: to retrieve the predicted clusters. -ARGUMENT:: action -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. -ARGUMENT:: action -A function to run when the server responds, taking the ID of the cluster as its argument. - -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 clusers with a mean per column. -ARGUMENT:: action -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:: - -( -//Make some clumped 2D points and place into a DataSet -~points = (4.collect{ - 64.collect{(1.sum3rand) + [1,-1].choose}.clump(2) - }).flatten(1) * 0.5; -fork{ - ~dataSet = FluidDataSet(s); - d = Dictionary.with( - *[\cols -> 2,\data -> Dictionary.newFrom( - ~points.collect{|x, i| [i, x]}.flatten)]); - s.sync; - ~dataSet.load(d, {~dataSet.print}); -} -) - - -// Create a KMeans instance and a LabelSet for the cluster labels in the server -~clusters = FluidLabelSet(s); -~kmeans = FluidKMeans(s); - -// Fit into 4 clusters -( -~kmeans.fitPredict(~dataSet,~clusters,action: {|c| - "Fitted.\n # Points in each cluster:".postln; - c.do{|x,i| - ("Cluster" + i + "->" + x.asInteger + "points").postln; - } - }); -) - -// Cols of kmeans should match DataSet, size is the number of clusters - -~kmeans.cols; -~kmeans.size; -~kmeans.dump; - -// Retrieve labels of clustered points -( -~assignments = Array.new(128); -fork{ - 128.do{ |i| - ~clusters.getLabel(i,{|clusterID| - (i.asString+clusterID).postln; - ~assignments.add(clusterID) - }); - s.sync; - } -} -) - -//or faster by sorting the IDs -~clusters.dump{|x|~assignments = x.at("data").atAll(x.at("data").keys.asArray.sort{|a,b|a.asInteger < b.asInteger}).flatten.postln;} - -//Visualise: we're hoping to see colours neatly mapped to quandrants... -( -d = ((~points + 1) * 0.5).flatten(1).unlace; -w = Window("scatter", Rect(128, 64, 200, 200)); -~colours = [Color.blue,Color.red,Color.green,Color.magenta]; -w.drawFunc = { - Pen.use { - d[0].size.do{|i| - var x = (d[0][i]*200); - var y = (d[1][i]*200); - var r = Rect(x,y,5,5); - Pen.fillColor = ~colours[~assignments[i].asInteger]; - Pen.fillOval(r); - } - } -}; -w.refresh; -w.front; -) - -// single point transform on arbitrary value -~inbuf = Buffer.loadCollection(s,0.5.dup); -~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 - -code:: -( -{ - var trig = Impulse.kr(5); - var point = WhiteNoise.kr(1.dup); - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(1); - Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; - ~kmeans.kr(trig,inputPoint,outputPoint); - Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); -}.play; -) - -// to sonify the output, here are random values alternating quadrant, generated more quickly as the cursor moves rightwards -( -{ - var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir / 2)); - var step = Stepper.kr(trig,max:3); - var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(1); - point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; - ~kmeans.kr(trig,inputPoint,outputPoint); - SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1); -}.play; -) - -:: diff --git a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp deleted file mode 100644 index 156aa56..0000000 --- a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp +++ /dev/null @@ -1,181 +0,0 @@ -TITLE:: FluidKNNClassifier -summary:: Classify data with K Nearest Neighbours -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidKNNRegressor, Classes/FluidDataSet, Classes/FluidLabelSet - -DESCRIPTION:: -A nearest-neighbour classifier using link::Classes/FluidKDTree:: . Each point is assigned the class that is most common among its nearest neighbours. -https://scikit-learn.org/stable/modules/neighbors.html#classification - -CLASSMETHODS:: - -METHOD:: new -Create a new KNNClassifier -ARGUMENT:: server -The server to make the model on -ARGUMENT:: numNeighbours -the number of neighours to consider -ARGUMENT:: weight -true / false: whether the neighbours should be weighted by distance - -INSTANCEMETHODS:: - -METHOD:: fit -Fit the model to a source link::Classes/FluidDataSet:: and a target link::Classes/FluidLabelSet::. These need to be the same size -ARGUMENT:: dataSet -Source data -ARGUMENT:: labelSet -Labels for the source data -ARGUMENT:: action -Run when done - -METHOD:: predict -Given a fitted model, predict labels for a link::Classes/FluidDataSet:: and write these to a link::Classes/FluidLabelSet:: -ARGUMENT:: dataSet -data to predict labels for -ARGUMENT:: labelSet -place to write labels -ARGUMENT:: action -Run when done - -METHOD:: predictPoint -Given a fitted model, predict labels for a data point in a link::Classes/Buffer:: and return these to the caller -ARGUMENT:: buffer -A data point -ARGUMENT:: action -Run when done, passes predicted label as argument - -EXAMPLES:: - -code:: - - -// Make: -// - A KNN Classifier -// - A DataSet of example points, and a LabelSet of corresponding labels -// - A DataSet of test data and a LabelSet for predicted labels - -( -~classifier = FluidKNNClassifier(s); -~source= FluidDataSet(s); -~labels = FluidLabelSet(s); -~test = FluidDataSet(s); -~mapping = FluidLabelSet(s); -) - -//Make some clumped 2D points and place into a DataSet -( -~examplepoints = [[0.5,0.5],[-0.5,0.5],[0.5,-0.5],[-0.5,-0.5]]; -~examplelabels = [\red,\orange,\green,\blue]; -d = Dictionary.new; -d.add(\cols -> 2); -d.add(\data -> Dictionary.newFrom(~examplepoints.collect{|x, i|[i.asString, x]}.flatten)); -~source.load(d); -~examplelabels.collect{|x,i| ~labels.addLabel(i, x);}; -) - -//Make some random, but clustered test points -( -~testpoints = (4.collect{ - 64.collect{(1.sum3rand) + [1,-1].choose}.clump(2) - }).flatten(1) * 0.5; -d = Dictionary.with( - *[\cols -> 2,\data -> Dictionary.newFrom( - ~testpoints.collect{|x, i| [i, x]}.flatten)]); - ~test.load(d); - -) - - -//Fit the classifier to the example DataSet and LabelSet, and then run prediction on the test data into our mapping LabelSet -( -~classifier.fit(~source,~labels); -~classifier.predict(~test, ~mapping, 1); -) - -//Return labels of clustered points - wait for the dump to be done -( -~assignments = Array.new(~testpoints.size); -fork{ - ~testpoints.do{|x,i| - ~mapping.getLabel(i, action:{|l| - ~assignments.add(l); - }); - s.sync; - if(i==(~testpoints.size - 1)){"Got assignments".postln;} - }; - ~assignments.postln; -} -) - -//Visualise: we're hoping to see colours neatly mapped to quandrants... -( -c = IdentityDictionary(); - -c.add(\red->Color.red); -c.add(\blue->Color.blue); -c.add(\green->Color.green); -c.add(\orange-> Color.new255(255, 127, 0)); - -e = 200 * ((~examplepoints + 1) * 0.5).flatten(1).unlace; -d = ((~testpoints + 1) * 0.5).flatten(1).unlace; -// d = [20.collect{1.0.rand}, 20.collect{1.0.rand}]; -w = Window("scatter", Rect(128, 64, 200, 200)); -~colours = [Color.blue,Color.red,Color.green,Color.magenta]; -w.drawFunc = { - Pen.use { - e[0].size.do{|i| - var r = Rect(e[0][i],e[1][i],10,10); - Pen.fillColor = c[~examplelabels[i]]; - Pen.fillOval(r); - }; - d[0].size.do{|i| - var x = (d[0][i]*200); - var y = (d[1][i]*200); - var r = Rect(x,y,5,5); - Pen.fillColor = c[~assignments[i].asSymbol].alpha_(0.3); - Pen.fillOval(r); - } - } -}; -w.refresh; -w.front; -) - -// single point prediction on arbitrary value -~inbuf = Buffer.loadCollection(s,0.5.dup); -~classifier.predictPoint(~inbuf,{|x|x.postln;}); -:: - -subsection::Server Side Queries -This is the equivalent of predictPoint, but wholly on the server -code:: - -//Generate a random point and sends a trigger to query, and return the class that point matches -( -{ - var trig = Impulse.kr(5); - var point = WhiteNoise.kr(1.dup); - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(1); - Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; - ~classifier.kr(trig,inputPoint,outputPoint); - Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); -}.play; -) - -// to sonify the output, here are random values alternating quadrant. -( -{ - var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll(trig:2, label: "Query Frequency")); - var step = Stepper.kr(trig,max:3); - var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(1); - point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; - ~classifier.kr(trig,inputPoint,outputPoint); - SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps, mul: 0.1); -}.play -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp deleted file mode 100644 index 6ca7b14..0000000 --- a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp +++ /dev/null @@ -1,131 +0,0 @@ -TITLE:: FluidKNNRegressor -summary:: Regression with K Nearest Neighbours -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidKNNClassifier, Classes/FluidDataSet - -DESCRIPTION:: -A nearest-neighbour regressor. A continuous value is predicted for each point as the (weighted) average value of its nearest neighbours. - -https://scikit-learn.org/stable/modules/neighbors.html#regression - -CLASSMETHODS:: - -METHOD:: new -Create a new KNN regressor on the server -ARGUMENT:: server -The server to run this model on. -ARGUMENT:: numNeighbours -number of neigbours to consider in mapping, min 1 -ARGUMENT:: weight -Whether to weight neighbours by distance when producing new point - -INSTANCEMETHODS:: - -METHOD:: fit -Map a source link::Classes/FluidDataSet:: to a one-dimensional target; both DataSets need to have the same number of points. -ARGUMENT:: sourceDataSet -Source data -ARGUMENT:: targetDataSet -Target data -ARGUMENT:: action -Run when done - - -METHOD:: predict -Apply learned mapping to a link::Classes/FluidDataSet:: and write to an output DataSet -ARGUMENT:: sourceDataSet -data to regress -ARGUMENT:: targetDataSet -output data -ARGUMENT:: action -Run when done - -METHOD:: predictPoint -Apply learned mapping to a data point in a link::Classes/Buffer:: -ARGUMENT:: buffer -data point -ARGUMENT:: action -Run when done - -EXAMPLES:: - -code:: - -//Make a simple mapping between a ramp and a sine cycle, test with an exponentional ramp -( -~source = FluidDataSet(s); -~target = FluidDataSet(s); -~test = FluidDataSet(s); -~output = FluidDataSet(s); -~tmpbuf = Buffer.alloc(s,1); -~regressor = FluidKNNRegressor(s); -) - -//Make source, target and test data -( -~sourcedata = 128.collect{|i|i/128}; -~targetdata = 128.collect{|i| sin(2*pi*i/128) }; -~testdata = 128.collect{|i|(i/128)**2}; - -~source.load( - Dictionary.with( - *[\cols -> 1,\data -> Dictionary.newFrom( - ~sourcedata.collect{|x, i| [i.asString, [x]]}.flatten)]) -); - -~target.load( -d = Dictionary.with( - *[\cols -> 1,\data -> Dictionary.newFrom( - ~targetdata.collect{|x, i| [i.asString, [x]]}.flatten)]); -); - -~test.load( - Dictionary.with( - *[\cols -> 1,\data -> Dictionary.newFrom( - ~testdata.collect{|x, i| [i.asString, [x]]}.flatten)]) -); - -~targetdata.plot; -~source.print; -~target.print; -~test.print; - -) - -// Now make a regressor and fit it to the source and target, and predict against test -//grab the output data whilst we're at it, so we can inspect -( -~outputdata = Array(128); -~regressor.fit(~source, ~target); -~regressor.predict(~test, ~output, 1, action:{ - ~output.dump{|x| 128.do{|i| - ~outputdata.add(x["data"][i.asString][0]) - }}; -}); -) - -//We should see a single cycle of a chirp -~outputdata.plot; - -// single point transform on arbitrary value -~inbuf = Buffer.loadCollection(s,[0.5]); -~regressor.predictPoint(~inbuf,{|x|x.postln;}); -:: - -subsection:: Server Side Queries - -code:: -//we are here querying with a saw in control rate, all on the server, via a buffer interface -( -{ - var input = Saw.kr(2).linlin(-1,1,0,1); - var trig = Impulse.kr(ControlRate.ir/10); - var inputPoint = LocalBuf(1); - var outputPoint = LocalBuf(1); - BufWr.kr(input,inputPoint,0); - ~regressor.kr(trig,inputPoint,outputPoint); - BufRd.kr(1,outputPoint,0); -}.scope -) - -:: diff --git a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp deleted file mode 100644 index 2319562..0000000 --- a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp +++ /dev/null @@ -1,75 +0,0 @@ -TITLE:: FluidLabelSet -summary:: A set of labels associated with IDs -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidDataSet, Classes/FluidKMeans - -DESCRIPTION:: -FluidLabelSet is a container associating identifiers with labels. - -CLASSMETHODS:: - -METHOD:: new -Make a new instance of a label set, uniquely identified by its name. Creating an instance with a name already in use will throw an exception. Use link::Classes/FluidLabelSet#*at:: or free the existing instance. -ARGUMENT:: server -The link::Classes/Server:: on which to create the label set. - - -INSTANCEMETHODS:: - -PRIVATE:: init, id - -METHOD:: addLabel -Add a label to the label set. -ARGUMENT:: identifier -The identifier for this label. -ARGUMENT:: label -The label to add. -ARGUMENT:: action -A function to run when the operation completes. - -METHOD:: updateLabel -Change a label in the label set. -ARGUMENT:: identifier -The identifier for this label. -ARGUMENT:: label -The label to update. -ARGUMENT:: action -A function to run when the operation completes. - -METHOD:: getLabel -Retrieve the label associated with an identifier. Will report an error if the identifier is not present in the set. -ARGUMENT:: identifier -The identifier for the label to be retrieved. -ARGUMENT:: action -A function to run when the operation completes. - -METHOD:: deleteLabel -Delete a label given a certain identifier. -ARGUMENT:: identifier -The identifier to be deleted. -ARGUMENT:: action -A function to run when the operation completes. - -METHOD:: clear -Empty the label set. - -METHOD:: getIds -Export to the labelset 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:: print -Post an abbreviated content of the label set in the window by default, but you can supply a custom action instead. - -EXAMPLES:: - -code:: -~ls = FluidLabelSet.new(s); - -["one", "two", "three"].collect{|x,i| ~ls.addLabel(i, x);}; -~ls.print; - -~ls.free; -:: diff --git a/release-packaging/HelpSource/Classes/FluidLoudness.schelp b/release-packaging/HelpSource/Classes/FluidLoudness.schelp deleted file mode 100644 index dced642..0000000 --- a/release-packaging/HelpSource/Classes/FluidLoudness.schelp +++ /dev/null @@ -1,161 +0,0 @@ -TITLE:: FluidLoudness -SUMMARY:: A Loudness and True-Peak Descriptor in Real-Time -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Classes/FluidBufLoudness,Classes/FluidPitch,Classes/FluidMelBands,Classes/FluidMFCC,Classes/FluidSpectralShape,Guides/FluidCorpusManipulationToolkit -DESCRIPTION:: - - - Two loudness descriptors, with a ITU BS 1770 mode - - - - Computes the true peak of the signal as well as applying the filters proposed by broadcasting standards to emulate the perception of amplitude. - - The process will return a multichannel control steam with [loudness, truepeak] values, both in dBFS, which will be repeated if no change happens within the algorithm, i.e. when the hopSize is larger than the signal vector size. More information on broadcasting standardisation of loudness measurement is available at the reference page (LINK::https://tech.ebu.ch/docs/tech/tech3341.pdf::) and in more musician-friendly explantions here (LINK::http://designingsound.org/2013/02/06/loudness-and-metering-part-1/::). - - -CLASSMETHODS:: - -METHOD:: kr - -ARGUMENT:: in - - Audio-rate signal to analyze - -ARGUMENT:: kWeighting - - - A flag to switch the perceptual model of loudness. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple RMS of the frame. - - -ARGUMENT:: truePeak - - - A flag to switch the computation of TruePeak. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple absolute peak of the frame. - - -ARGUMENT:: windowSize - - - The size of the window on which the computation is done. By default 1024 to be similar with all other FluCoMa objects, the EBU specifies 400ms, which is 17640 samples at 44100. - - STRONG::Constraints:: - - LIST:: - ## - Maximum: CODE::maxWindowSize:: - - :: - -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 100ms, which is 4410 samples at 44100. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 1 - - :: - - -ARGUMENT:: maxWindowSize - - - How large can the windowSize be, by allocating memory at instantiation time. This cannot be modulated. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 4 - - ## - Snaps to powers of two - - :: - - - -INSTANCEMETHODS:: - -EXAMPLES:: - - -code:: -//create a monitoring bus for the descriptors -b = Bus.new(\control,0,2); - -//create a monitoring window for the values -( -w = Window("Loudness Monitor", Rect(10, 10, 220, 65)).front; - -c = Array.fill(2, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("Loudness: "); -c[1].string = ("Peak: "); - -a = Array.fill(2, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); -) - -//routine to update the parameters -( -r = Routine { - { - b.get({ arg val; - { - if(w.isClosed.not) { - val.do({arg item,index; - a[index].string = item.round(0.01)}) - } - }.defer - }); - 0.1.wait; - }.loop -}.play -) - -//basic test, with default values -( -x = {var source = PinkNoise.ar(0.25); - Out.kr(b, FluidLoudness.kr(source)); - source.dup; -}.play; -) - -//free this -x.free - -//the EBU standard specifies that the window should be 400ms long, and update every 100ms, for instantaneous loudness. At SR=44100, this means the following settings. Various test signals are loaded. -( -x = { - arg freq=220, type = 1, noise = 0; - var source = PinkNoise.ar(noise) + Select.ar(type,[DC.ar(),SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1)]); - Out.kr(b, FluidLoudness.kr(source,windowSize:17640,hopSize:4410,maxwindowSize:17640)); - source.dup; -}.play; -) -// change the various frequencies to see the impact of the filter for the loudness. The TruePeak is steady. -x.set(\freq, 440) -x.set(\freq, 110) -x.set(\freq, 55) -x.set(\freq, 3000) -x.set(\freq, 9000) - -// adding harmonics, by changing to triangle (2), saw (3) or square (4) shows that spectral algo are more resilient when signal are richer -x.set(\type, 2) -x.set(\type, 3) -x.set(\type, 4) - -// adding noise shows its impact on loudness -x.set(\noise, 0.25) - -// and removing the oscilator -x.set(\type, 0) - -// and measuring silence -x.set(\noise, 0) -:: diff --git a/release-packaging/HelpSource/Classes/FluidMDS.schelp b/release-packaging/HelpSource/Classes/FluidMDS.schelp deleted file mode 100644 index 4e971f2..0000000 --- a/release-packaging/HelpSource/Classes/FluidMDS.schelp +++ /dev/null @@ -1,176 +0,0 @@ -TITLE:: FluidMDS -summary:: Dimensionality Reduction with Multidimensional Scaling -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidPCA, Classes/FluidDataSet - -DESCRIPTION:: - -Multidimensional scaling of a link::Classes/FluidDataSet:: - -https://scikit-learn.org/stable/modules/manifold.html#multi-dimensional-scaling-mds - - -CLASSMETHODS:: - - -METHOD:: new -Make a new instance -ARGUMENT:: server -The server on which to run this model -ARGUMENT:: numDimensions -The number of dimensions to reduce to -ARGUMENT:: distanceMetric -The distance metric to use (integer, 0-6, see utility constants below) - -METHOD:: euclidean -Euclidean distance (default) - -METHOD:: sqeuclidean -Squared Euclidean distance - -METHOD:: manhattan -Manhattan distance - -METHOD:: max -Minkowski max - -METHOD:: min -Minkowski max - -METHOD:: kl -Symmetric Kulback Leiber divergance (only makes sense with non-negative data) - -METHOD:: cosine -Cosine distance - -INSTANCEMETHODS:: - -PRIVATE:: init - -METHOD:: fitTransform -Fit the model to a link::Classes/FluidDataSet:: and write the new projected data to a destination FluidDataSet. -ARGUMENT:: sourceDataSet -Source data, or the DataSet name -ARGUMENT:: destDataSet -Destination data, or the DataSet name -ARGUMENT:: action -Run when done - -EXAMPLES:: - -code:: - -//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidMDS -( -~audiofile = FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav"); -~raw = FluidDataSet(s); -~standardized = FluidDataSet(s); -~reduced = FluidDataSet(s); -~audio = Buffer.read(s,~audiofile); -~mfcc_feature = Buffer.new(s); -~stats = Buffer.alloc(s, 7, 12); -~standardizer = FluidStandardize(s); -~mds = FluidMDS(s); -) - -// Load audio and run an mfcc analysis, which gives us 13 points (we'll throw the 0th away) -( -~audio = Buffer.read(s,~audiofile); -FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); -) - -// Divide the time series in 100, and take the mean of each segment and add this as a point to -// the 'raw' FluidDataSet -( -{ - var trig = LocalIn.kr(1, 1); - var buf = LocalBuf(12, 1); - var count = PulseCount.kr(trig) - 1; - 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 - ); - 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, idNumber: count, trig: Done.kr(stats),blocking:1); - LocalOut.kr(Done.kr(dsWr)); - FreeSelf.kr(count - 99); - Poll.kr(trig,(100-count)); -}.play; -) -// wait for the count to reaches 0 in the post window. - -//First standardize our DataSet, so that the MFCC dimensions are on comensurate scales -//Then apply the MDS in-place on the standardized data to get 2 dimensions, using a Euclidean distance metric -//Download the DataSet contents into an array for plotting -( -~reducedarray = Array.new(100); -~standardizer.fitTransform(~raw, ~standardized); -~mds.fitTransform(~standardized, ~reduced, action:{ - ~reduced.dump{|x| 100.do{|i| - ~reducedarray.add(x["data"][i.asString]) -}}}); -) - -//Visualise the 2D projection of our original 12D data -( -d = ~reducedarray.flop.deepCollect(1, { |x| x.normalize}); -w = Window("scatter", Rect(128, 64, 200, 200)); -w.drawFunc = { - Pen.use { - d[0].size.do{|i| - var x = (d[0][i]*200); - var y = (d[1][i]*200); - var r = Rect(x,y,5,5); - Pen.fillColor = Color.blue; - Pen.fillOval(r); - } - } -}; -w.refresh; -w.front; -) - -//we can change the distance computation -~mds.distanceMetric = FluidMDS.kl; - -//recompute the reduction and recover the points -( -~reducedarray2 = Array.new(100); -~mds.fitTransform(~standardized, ~reduced, action:{ - ~reduced.dump{|x| 100.do{|i| - ~reducedarray2.add(x["data"][i.asString]) - }}}); -) - -//draw the new projection in red above the other -//Visualise the 2D projection of our original 12D data -( -d = ~reducedarray.flop.deepCollect(1, { |x| x.normalize}); -e = ~reducedarray2.flop.deepCollect(1, { |x| x.normalize}); -w.drawFunc = { - Pen.use { - d[0].size.do{|i| - var x = (d[0][i]*200); - var y = (d[1][i]*200); - var r = Rect(x,y,5,5); - Pen.fillColor = Color.blue; - Pen.fillOval(r); - }; - e[0].size.do{|i| - var x = (e[0][i]*200); - var y = (e[1][i]*200); - var r = Rect(x,y,5,5); - Pen.fillColor = Color.red; - Pen.fillOval(r); - } - } -}; -w.refresh; -w.front; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp deleted file mode 100644 index 2021e34..0000000 --- a/release-packaging/HelpSource/Classes/FluidMFCC.schelp +++ /dev/null @@ -1,383 +0,0 @@ -TITLE:: FluidMFCC -SUMMARY:: Mel-Frequency Cepstral Coefficients as Spectral Descriptors in Real-Time -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Classes/FluidBufMFCC,Classes/FluidPitch,Classes/FluidMelBands,Classes/FluidLoudness,Classes/FluidSpectralShape,Guides/FluidCorpusManipulationToolkit -DESCRIPTION:: - - - This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (MFCCs) - - - - See LINK::https://en.wikipedia.org/wiki/Mel-frequency_cepstrum::. The input is first decomposed into perceptually spaced bands (the number of bands specified by numBands), just as in the MelBands object. It is then analysed in numCoefs number of cepstral coefficients. It has the avantage to be amplitude invarient, except for the first coefficient. - - The process will return a multichannel control steam of maxNumCoeffs, which will be repeated if no change happens within the algorithm, i.e. when the hopSize is larger than the host vector size. - - -CLASSMETHODS:: - -METHOD:: kr - -ARGUMENT:: in - - Audio-rate signal to analyze - -ARGUMENT:: numCoeffs - - - The number of cepstral coefficients to be outputed. It is limited by the maxNumCoefs parameter. When the number is smaller than the maximum, the output is zero-padded. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 2 - - ## - Maximum: MIN(CODE::numBands::, CODE::maxNumCoeffs::) - - :: - -ARGUMENT:: numBands - - - The number of bands that will be perceptually equally distributed between minFreq and maxFreq to describe the spectral shape before it is converted to cepstral coefficients. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: MAX(CODE::numCoeffs::, 2) - - ## - Maximum: CODE::(FFT Size / 2) + 1:: (see fft settings) - - :: - -ARGUMENT:: startCoeff - - - The lowest index of the output cepstral coefficient, zero-counting. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - ## - Maximum: 1 - - :: - -ARGUMENT:: minFreq - - - The lower boundary of the lowest band of the model, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: maxFreq - - - The highest boundary of the highest band of the model, in Hz. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 0 - - :: - -ARGUMENT:: windowSize - - - The window size. As MFCC computation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: - - -ARGUMENT:: hopSize - - - The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - - -ARGUMENT:: fftSize - - - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. - - - -ARGUMENT:: maxNumCoeffs - - - The maximum number of cepstral coefficients that can be computed. This sets the number of channels of the output, and therefore cannot be modulated. - - STRONG::Constraints:: - - LIST:: - ## - Minimum: 2 - - ## - Maximum: (max FFFT Size / 2) + 1`` (see maxFFTSize) - - :: - -ARGUMENT:: maxFFTSize - - - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - - - - -INSTANCEMETHODS:: - -EXAMPLES:: - -code:: -//create a monitoring window for the values - -( -b = Bus.new(\control,0,13); -w = Window("MFCCs Monitor", Rect(10, 10, 420, 320)).front; -a = MultiSliderView(w,Rect(10, 10, 400, 300)).elasticMode_(1).isFilled_(1); -a.reference_(Array.fill(13,{0.5})); //make a center line to show 0 -) - -//run the window updating routine. -( -~winRange = 20; - -r = Routine { - { - b.get({ arg val; - { - if(w.isClosed.not) { - //val.postln; - a.value = val.linlin(~winRange.neg,~winRange,0,1); - } - }.defer - }); - 0.01.wait; - }.loop -}.play -) - -//play a simple sound to observe the values -( -x = {arg type = 0; - var source = Select.ar(type,[SinOsc.ar(220),Saw.ar(220),Pulse.ar(220)]) * LFTri.kr(0.1).exprange(0.01,0.1); - Out.kr(b,FluidMFCC.kr(source,maxNumCoeffs:13)); - source.dup; -}.play; -) - -// change the wave types, observe the amplitude invariance of the descriptors, apart from the leftmost coefficient -x.set(\type, 1) -~winRange = 40; //adjust the range above and below 0 to zoom in or out on the MFCC -x.set(\type, 2) -x.set(\type, 0) -// free this source -x.free - -// load a more exciting one -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); - -// analyse with parameters to be changed -( -x = {arg bands = 40, low = 20, high = 20000; - var source = PlayBuf.ar(1,c,loop:1); - Out.kr(b,FluidMFCC.kr(source, numCoeffs: 13, numBands: bands, minFreq: low, maxFreq: high, maxNumCoeffs: 13) / 10); - source.dup; -}.play; -) -~winRange = 10; //adjust the range above and below 0 to zoom in or out on the MFCC - -// observe the number of bands. The unused ones at the top are not updated -x.set(\bands,20) - -// back to the full range -x.set(\bands,40) - -// focus all the bands on a mid range -x.set(\low,320, \high, 800) - -// focusing on the low end shows the fft resolution issue. One could restart the analysis with a larger fft to show more precision -x.set(\low,20, \high, 160) - -// back to full range -x.set(\low,20, \high, 20000) - -// free everything -x.free;b.free;c.free;r.stop; -:: - -STRONG::A musical example:: - -CODE:: -//program that freezes mfcc spectra, then looks for matches between two frozen spectra -( -SynthDef("MFCCJamz", {arg freq=220, source = 0, buffer, mfccBus, distBus, t_freeze0=0, t_freeze1=0, onsetsOn0=0, onsetsOn1=0, restart = 1; - var sound, mfcc, mfccFreeze0, mfccFreeze1, dist0, dist1, closest, slice; - - sound = SelectX.ar(source, [ - SinOsc.ar(freq, 0, 0.1), - LFTri.ar(freq, 0, 0.1), - LFSaw.ar(freq, 0, 0.1), - Pulse.ar(freq, 0.5, 0.1), - WhiteNoise.ar(0.1), - PinkNoise.ar(0.1), - PlayBuf.ar(1, buffer, 1, loop:1, trigger:restart) - ]); - - slice = FluidOnsetSlice.ar(sound); //onset detection for mfcc freeze on onset - - mfcc = FluidMFCC.kr(sound,maxNumCoeffs:13); - mfccFreeze0 = Latch.kr(mfcc, t_freeze0+(slice*onsetsOn0)); - mfccFreeze1 = Latch.kr(mfcc, t_freeze1+(slice*onsetsOn1)); - - Out.kr(mfccBus,mfcc.addAll(mfccFreeze0).addAll(mfccFreeze1)); - - //distance calculations - - dist0 = Mix((mfcc.copyRange(1,12) - mfccFreeze0.copyRange(1,12)).squared).sqrt; - dist1 = Mix((mfcc.copyRange(1,12) - mfccFreeze1.copyRange(1,12)).squared).sqrt; - - Out.kr(distBus, [dist0, dist1]); - - //sends a trigger when the item with a closer euclidean distance changes - SendTrig.kr(Trig1.kr(dist1-dist0, 0.001)+Trig1.kr(dist0-dist1, 0.001), 0, dist1FluidCorpusManipulation -related:: Classes/FluidMLPRegressor, Classes/FluidDataSet - -Perform classification between a link::Classes/FluidDataSet:: and a link::Classes/FluidLabelSet:: using a Multi-Layer Perception neural network. - -CLASSMETHODS:: - -METHOD:: new -Creates a new instance on the server. - -ARGUMENT:: server -The link::Classes/Server:: on which to run this model. - -ARGUMENT:: hidden -An link::Classes/Array:: that gives the sizes of any hidden layers in the network (default is two hidden layers of three units each). - -ARGUMENT:: activation -The activation function to use for the hidden layer units. Beware of the permitted ranges of each: relu (0->inf), sigmoid (0->1), tanh (-1,1). - -ARGUMENT:: maxIter -The maximum number of iterations to use in training. - -ARGUMENT:: learnRate -The learning rate of the network. Start small, increase slowly. - -ARGUMENT:: momentum -The training momentum, default 0.9 - -ARGUMENT:: batchSize -The training batch size. - -ARGUMENT:: validation -The fraction of the DataSet size to hold back during training to validate the network against. - -METHOD:: identity, sigmoid, relu, tanh -A set of convinience constants for the available activation functions. - -INSTANCEMETHODS:: - -PRIVATE:: init, uid - -METHOD:: fit -Train the network to map between a source link::Classes/FluidDataSet:: and a target link::Classes/FluidLabelSet:: - -ARGUMENT:: sourceDataSet -Source data - -ARGUMENT:: targetLabelSet -Target data - -ARGUMENT:: action -Function to run when training is complete - -returns:: The training loss, or -1 if training failed - -METHOD:: predict -Apply the learned mapping to a DataSet (given a trained network) - -ARGUMENT:: sourceDataSet -Input data - -ARGUMENT:: targetLabelSet -Output data - -ARGUMENT:: action -Function to run when complete - -METHOD:: predictPoint -Apply the learned mapping to a single data point in a link::Classes/Buffer:: - -ARGUMENT:: sourceBuffer -Input point - -ARGUMENT:: action -A function to run when complete - -METHOD:: clear -This will erase all the learning done in the neural network. - -ARGUMENT:: action -A function to run when complete - -EXAMPLES:: - -code:: -( -~classifier=FluidMLPClassifier(s).hidden_([6]).activation_(FluidMLPClassifier.tanh).maxIter_(1000).learnRate_(0.1).momentum_(0.1).batchSize_(50).validation_(0); -~sourcedata= FluidDataSet(s); -~labels = FluidLabelSet(s); -~testdata = FluidDataSet(s); -~predictedlabels = FluidLabelSet(s); -) -//Make some clumped 2D points and place into a DataSet -( -~centroids = [[0.5,0.5],[-0.5,0.5],[0.5,-0.5],[-0.5,-0.5]]; -~categories = [\red,\orange,\green,\blue]; -~trainingset = Dictionary(); -~labeldata = Dictionary(); -4.do{ |i| - 64.do{ |j| - ~trainingset.put("mlpclass"++i++\_++j, ~centroids[i].collect{|x| x.gauss(0.5/3)}); - ~labeldata.put("mlpclass"++i++\_++j,[~categories[i]]); - } -}; -~sourcedata.load(Dictionary.with(*[\cols->2,\data->~trainingset]),action:{~sourcedata.print}); -~labels.load(Dictionary.with(*[\cols->1,\data->~labeldata]),action:{~labels.print}); -) - -//Fit the classifier to the example DataSet and labels, and then run prediction on the test data into our mapping label set -~classifier.fit(~sourcedata,~labels,action:{|loss| ("Trained"+loss).postln}); - -//make some test data -( -~testset = Dictionary(); -4.do{ |i| - 64.do{ |j| - ~testset.put("mlpclass_test"++i++\_++j, ~centroids[i].collect{|x| x.gauss(0.5/3)}); - } -}; -~testdata.load(Dictionary.with(*[\cols->2,\data->~testset]),action:{~testdata.print}); -) - -//Run the test data through the network, into the predicted labelset -~classifier.predict(~testdata,~predictedlabels,action:{"Test complete".postln}); - -//get labels from server -~predictedlabels.dump(action:{|d| ~labelsdict = d["data"]; ~labelsdict.postln}); - -//Visualise: we're hoping to see colours neatly mapped to quandrants... -( -c = Dictionary(); -c.add("red"->Color.red); -c.add("blue"->Color.blue); -c.add("green"->Color.green); -c.add("orange"->Color.new255(255, 127, 0)); -e = 200 * ((~centroids + 1) * 0.5).flatten(1).unlace; -w = Window("scatter", Rect(128, 64, 200, 200)); -w.drawFunc = { - Pen.use { - ~testset.keysValuesDo{|k,v| - var x = v[0].linlin(-1,1,200,0).asInteger; - var y = v[1].linlin(-1,1,200,0).asInteger; - var r = Rect(x,y,5,5); - Pen.fillColor = c.at(~labelsdict[k][0]); - Pen.fillOval(r); - } - } -}; -w.refresh; -w.front; -) - -// single point transform on arbitrary value -~inbuf = Buffer.loadCollection(s,0.5.dup); -~classifier.predictPoint(~inbuf,{|x|x.postln;}); -:: -subsection::Querying in a Synth -This is the equivalent of code::predictPoint::, but wholly on the server -code:: -( -{ - var trig = Impulse.kr(5); - var point = WhiteNoise.kr(1.dup); - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(1); - Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; - ~classifier.kr(trig,inputPoint,outputPoint); - Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); - Silent.ar; -}.play; -) - -// to sonify the output, here are random values alternating quadrant. -( -{ - var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll(trig: 2,label: "Query Frequency")); - var step = Stepper.kr(trig,max:3); - var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(1); - point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; - ~classifier.kr(trig,inputPoint,outputPoint); - SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1) -}.play; -) -:: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp b/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp deleted file mode 100644 index 5a0c28b..0000000 --- a/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp +++ /dev/null @@ -1,191 +0,0 @@ -TITLE:: FluidMLPRegressor -summary:: Regression with a multi-layer perceptron -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidMLPClassifier, Classes/FluidDataSet - -DESCRIPTION:: -Perform regression between link::Classes/FluidDataSet::s using a Multi-Layer Perception neural network. - -CLASSMETHODS:: - -METHOD:: new -Creates a new instance on the server. - -ARGUMENT:: server -The link::Classes/Server:: on which to run this model. - -ARGUMENT:: hidden -An link::Classes/Array:: that gives the sizes of any hidden layers in the network (default is two hidden layers of three units each). - -ARGUMENT:: activation -The activation function to use for the hidden layer units. Beware of the permitted ranges of each: relu (0->inf), sigmoid (0->1), tanh (-1,1). - -ARGUMENT:: outputActivation -The activation function to use for the final layer units. Beware of the permitted ranges of each: relu (0->inf), sigmoid (0->1), tanh (-1,1). - -ARGUMENT:: tapIn -The layer whose input is used to predict and predictPoint. It is 0 counting, where the default of 0 is the input layer, and 1 would be the first hidden layer, and so on. - -ARGUMENT:: tapOut -The layer whose output to return. It is counting from 0 as the input layer, and 1 would be the first hidden layer, and so on. The default of -1 is the last layer of the whole network. - -ARGUMENT:: maxIter -The maximum number of iterations to use in training. - -ARGUMENT:: learnRate -The learning rate of the network. Start small, increase slowly. - -ARGUMENT:: momentum -The training momentum, default 0.9 - -ARGUMENT:: batchSize -The training batch size. - -ARGUMENT:: validation -The fraction of the DataSet size to hold back during training to validate the network against. - -METHOD:: identity, sigmoid, relu, tanh -A set of convinience constants for the available activation functions. - -INSTANCEMETHODS:: - -PRIVATE:: init, uid - -METHOD:: fit -Train the network to map between a source and target link::Classes/FluidDataSet:: - -ARGUMENT:: sourceDataSet -Source data - -ARGUMENT:: targetDataSet -Target data - -ARGUMENT:: action -Function to run when training is complete - -returns:: The training loss, or -1 if training failed - -METHOD:: predict -Apply the learned mapping to a link::Classes/FluidDataSet:: (given a trained network) - -ARGUMENT:: sourceDataSet -Input data - -ARGUMENT:: targetDataSet -Output data - -ARGUMENT:: action -Function to run when complete - -METHOD:: predictPoint -Apply the learned mapping to a single data point in a link::Classes/Buffer:: - -ARGUMENT:: sourceBuffer -Input point - -ARGUMENT:: targetBuffer -Output point - -ARGUMENT:: action -A function to run when complete - -METHOD:: clear -This will erase all the learning done in the neural network. - -ARGUMENT:: action -A function to run when complete - -EXAMPLES:: - -code:: - -//Make a simple mapping between a ramp and a sine cycle, test with an exponentional ramp -( -~source = FluidDataSet(s); -~target = FluidDataSet(s); -~test = FluidDataSet(s); -~output = FluidDataSet(s); -~tmpbuf = Buffer.alloc(s,1); -~regressor=FluidMLPRegressor(s).hidden_([2]).activation_(FluidMLPRegressor.tanh).outputActivation_(FluidMLPRegressor.tanh).maxIter_(1000).learnRate_(0.1).momentum_(0.1).batchSize_(1).validation_(0); -) - -//Make source, target and test data -( -~sourcedata = 128.collect{|i|i/128}; -~targetdata = 128.collect{|i| sin(2*pi*i/128) }; -~testdata = 128.collect{|i|(i/128)**2}; - -~source.load( - Dictionary.with( - *[\cols -> 1,\data -> Dictionary.newFrom( - ~sourcedata.collect{|x, i| [i.asString, [x]]}.flatten)]); -); - -~target.load( -d = Dictionary.with( - *[\cols -> 1,\data -> Dictionary.newFrom( - ~targetdata.collect{|x, i| [i.asString, [x]]}.flatten)]); -); - -~test.load( - Dictionary.with( - *[\cols -> 1,\data -> Dictionary.newFrom( - ~testdata.collect{|x, i| [i.asString, [x]]}.flatten)]); -); - -~targetdata.plot; -~source.print; -~target.print; -~test.print; -) - -// Now make a regressor and fit it to the source and target, and predict against test -//grab the output data whilst we're at it, so we can inspect - -// run this to train the network for up to 1000(max epochs to map source to target. fit() returns loss. If this is -1, then training has failed. Run until the printed error is satisfactory to you -~regressor.fit(~source, ~target, {|x|x.postln;}); - -//you can change parameters of the MLPregressor with setters -~regressor.learnRate = 0.01; -~regressor.momentum = 0; -~regressor.validation= 0.2; - -( -~outputdata = Array(128); -~regressor.predict(~test, ~output, action:{ - ~output.dump{|x| 128.do{|i| - ~outputdata.add(x["data"][i.asString][0]) - }}; -} -); -) - -//We should see a single cycle of a chirp. If not, fit a little more epochs -~outputdata.plot; - -// single point transform on arbitrary value -~inbuf = Buffer.loadCollection(s,[0.5]); -~outbuf = Buffer.new(s); -~regressor.predictPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,1,{|y|y.postln;};)}); -:: - -subsection:: Querying in a Synth - -This is the equivalent of calling code::predictPoint::, except wholly on the server - -code:: -( -{ - var input = Saw.kr(2).linlin(-1,1,0,1); - var trig = Impulse.kr(ControlRate.ir/10); - var inputPoint = LocalBuf(1); - var outputPoint = LocalBuf(1); - BufWr.kr(input,inputPoint,0); - ~regressor.kr(trig,inputPoint,outputPoint,0,-1); - Poll.kr(trig,BufRd.kr(1,outputPoint,0),"mapped value"); -}.scope; -) - - - -:: diff --git a/release-packaging/HelpSource/Classes/FluidMelBands.schelp b/release-packaging/HelpSource/Classes/FluidMelBands.schelp deleted file mode 100644 index 3674eae..0000000 --- a/release-packaging/HelpSource/Classes/FluidMelBands.schelp +++ /dev/null @@ -1,207 +0,0 @@ -TITLE:: FluidMelBands -SUMMARY:: A Perceptually Spread Spectral Contour Descriptor in Real-Time -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Classes/FluidMFCC - -DESCRIPTION:: -This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return a multichannel control steam of size STRONG::maxNumBands::, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. - -CLASSMETHODS:: - -METHOD:: kr - The audio rate in, control rate out version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: numBands - The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. It is limited by the STRONG::maxNumBands:: parameter. When the number is smaller than the maximum, the output is zero-padded. - -ARGUMENT:: minFreq - The lower boundary of the lowest band of the model, in Hz. - -ARGUMENT:: maxFreq - The highest boundary of the highest band of the model, in Hz. - -ARGUMENT:: normalize - This flag enables the scaling of the output to preserve the energy of the window. It is on (1) by default. - -ARGUMENT:: scale - This flag sets the scaling of the output value. It is either linear (0, by default) or in dB (1). - -ARGUMENT:: windowSize - The window size. As spectral description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -ARGUMENT:: maxNumBands - The maximum number of Mel bands that can be modelled. This sets the number of channels of the output, and therefore cannot be modulated. - -RETURNS:: - A KR signal of STRONG::maxNumBands:: channels, giving the measure amplitudes for each band. The latency is windowSize. - - -EXAMPLES:: - -code:: -//create a monitoring bus for the descriptors -b = Bus.new(\control,0,40); - -//create a monitoring window for the values - -( -w = Window("Mel Bands Monitor", Rect(10, 10, 620, 320)).front; -a = MultiSliderView(w,Rect(10, 10, 600, 300)).elasticMode_(1).isFilled_(1); -) - -//run the window updating routine. -( -~winRange = 0.1; -r = Routine { - { - b.get({ arg val; - { - if(w.isClosed.not) { - a.value = val/~winRange; - } - }.defer - }); - 0.01.wait; - }.loop -}.play -) - -//play a simple sound to observe the values -( -x = { - var source = SinOsc.ar(LFTri.kr(0.1).exprange(80,800),0,0.1); - Out.kr(b,FluidMelBands.kr(source,maxNumBands:40)); - source.dup; -}.play; -) - -// free this source -x.free - -// load a more exciting one -c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); - -// analyse with parameters to be changed -( -x = {arg bands = 40, low = 20, high = 20000; - var source = PlayBuf.ar(1,c,loop:1); - Out.kr(b,FluidMelBands.kr(source, bands, low, high, 40) / 10); - source.dup; -}.play; -) - -//set the winRange to a more informative value -~winRange = 0.05; - -// observe the number of bands. The unused ones at the top are not updated -x.set(\bands,20) - -// back to the full range -x.set(\bands,40) - -// focus all the bands on a mid range: nothing to see! -x.set(\low,320, \high, 800) - -// focusing on the low end shows the fft resolution issue. One could restart the analysis with a larger fft to show more precision -x.set(\low,20, \high, 160) - -// back to full range -x.set(\low,20, \high, 20000) - -// free everything -x.free;b.free;c.free;r.stop; -:: - -STRONG::A musical example: a perceptually spread vocoder:: - -CODE:: -//load a source and define control bus for the resynthesis cluster -( -b = Bus.control(s,40); -c = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -d = Group.new; -) - -//play the source and send the analysis on the -( -x = { - arg dry = 0.2; - var source = PlayBuf.ar(1,c,loop:1); - Out.kr(b,FluidMelBands.kr(source,maxNumBands:40)); - Out.ar(0, DelayN.ar(source,delaytime:1024*SampleDur.ir,mul:dry)); -}.play; -) - -// set the dry playback volume -x.set(\dry, 0.5) - -// create a cluster of sines tuned on each MelBand center frequency, as a sort of vocoder. -( -var lowMel = 1127.010498 * ((20/700) + 1).log; -var highMel = 1127.010498 * ((20000/700) + 1).log; -var rangeMel = highMel - lowMel; -var stepMel = rangeMel / 41; -40.do({ - arg i; - var freqMel = (stepMel * (i +1)) + lowMel; - var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700; - {SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.5))}.play(d,1,addAction:\addToTail); -}); -) - -// free all -d.free; x.free; b.free; c.free; - -///////////////////////////////////// -// instantiate a more dynamic vocoder: -// MouseX defines the bottom frequency and MouseY define the top frequency, between which the 40 bands of analysis and synthesis are perceptually equally spred - -// the bus, source and group -( -b = Bus.control(s,40); -c = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -d = Group.new; -) - -// the modified source -( -x = { - arg dry = 0.2; - var source = PlayBuf.ar(1,c,loop:1); - Out.kr(b,FluidMelBands.kr(source,maxNumBands:40,minFreq:MouseX.kr().exprange(20,600),maxFreq:MouseY.kr().exprange(650,20000))); - Out.ar(0, DelayN.ar(source,delaytime:1024*SampleDur.ir,mul:dry)); -}.play; -) - -// the modified vocoder -( -40.do({ - arg i; - { - var lowMel = 1127.010498 * ((MouseX.kr().exprange(20,600)/700) + 1).log; - var highMel = 1127.010498 * ((MouseY.kr().exprange(650,20000)/700) + 1).log; - var rangeMel = highMel - lowMel; - var stepMel = rangeMel / 41; - var freqMel = (stepMel * (i +1)) + lowMel; - var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700; - SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.5))}.play(d,1,addAction:\addToTail); -}); -) - -// free all -d.free; x.free; b.free; c.free; -:: diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp deleted file mode 100644 index 4001cef..0000000 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ /dev/null @@ -1,234 +0,0 @@ -TITLE:: FluidNMFFilter -SUMMARY:: Real-Time Non-Negative Matrix Factorisation with Fixed Bases -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Classes/FluidBufNMF, Classes/FluidNMFMatch - -DESCRIPTION:: -The FluidNMFFilter object decomposes and resynthesises an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: - -It outputs at AR the resynthesis of the best factorisation. The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition (number of components) is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxComponents:: parameter. - -NMF has been a popular technique in signal processing research for things like source separation and transcription footnote:: Smaragdis and Brown, Non-Negative Matrix Factorization for Polyphonic Music Transcription.::, although its creative potential is so far relatively unexplored. It works iteratively, by trying to find a combination of amplitudes ('activations') that yield the original magnitude spectrogram of the audio input when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results. - -The whole process can be related to a channel vocoder where, instead of fixed bandpass filters, we get more complex filter shapes and the activations correspond to channel envelopes. - -FluidNMFFilter is part of the LINK:: Guides/FluidCorpusManipulation::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - -CLASSMETHODS:: - -METHOD:: ar -The real-time processing method. It takes an audio input, and will yield a audio stream in the form of a multichannel array of size STRONG::maxComponents:: . If the bases buffer has fewer than maxComponents channels, the remaining outputs will be zeroed. - -ARGUMENT:: in -The signal input to the factorisation process. - -ARGUMENT:: bases - The server index of the buffer containing the different bases that the input signal will be matched against. Bases must be STRONG::(fft size / 2) + 1:: frames. If the buffer has more than STRONG::maxComponents:: channels, the excess will be ignored. - -ARGUMENT::maxComponents - The maximum number of elements the NMF algorithm will try to divide the spectrogram of the source in. This dictates the number of output channels for the ugen. This cannot be modulated. - -ARGUMENT:: iterations - The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU intensive, lower numbers will be more unpredictable in quality. - -ARGUMENT:: windowSize - The number of samples that are analysed at a time. A lower number yields greater temporal resolution, at the expense of spectral resoultion, and vice-versa. - -ARGUMENT:: hopSize - The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - A multichannel kr output, giving for each basis component the activation amount. - - -EXAMPLES:: -STRONG::A didactic example:: -CODE:: -( -// create buffers -b= Buffer.alloc(s,44100); -c = Buffer.alloc(s, 44100); -d = Buffer.new(s); -e= Buffer.new(s); -) - -( -// fill them with 2 clearly segregated sine waves and composite a buffer where they are consecutive -Routine { - b.sine2([500],[1], false, false); - c.sine2([5000],[1],false, false); - s.sync; - FluidBufCompose.process(s,b, destination:d); - FluidBufCompose.process(s,c, destStartFrame:44100, destination:d, destGain:1); - s.sync; - d.query; -}.play; -) - -// check -d.plot -d.play //////(beware !!!! loud!!!) - -( -// separate them in 2 components -Routine { - FluidBufNMF.process(s, d, bases: e, components:2).wait; - e.query; -}.play -) - -// check for 2 spikes in the spectra -e.plot - -//listen how the filter isolates each component and places them in each channel separately. -{FluidNMFFilter.ar(SinOsc.ar(500, mul: 0.1),e,2)}.play - -{FluidNMFFilter.ar(SinOsc.ar(5000, mul: 0.1),e,2)}.play - -{FluidNMFFilter.ar(SinOsc.ar([500,5000], mul: 0.1).sum,e,2)}.play -:: - -STRONG::A guitar processor:: -CODE:: -//set some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -c = Buffer.new(s); -~bases = Buffer.new(s); -~spectralshapes = Buffer.new(s); -~stats = Buffer.new(s); -~centroids = Buffer.new(s); -~trainedBases = Buffer.new(s); -) - -// train only 2 seconds -( -Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048).wait; - c.query; -}.play; -) - -// wait for the query to print -// find the component that has the picking sound checking the median spectral centroid -( -FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ |shapes| - FluidBufStats.process(s,shapes,stats:~stats, action:{|stats| - stats.getn(0, (stats.numChannels * stats.numFrames), {|x| - ~centroids = x.select({ - |item, index| (index.mod(7) == 0) && (index.div(70) == 5); - }); - ~centroids.postln; - }) - }) -}); -) - -// we then copy the basis with the lowest median centroid to a channel, and all the other bases to the other channel, of a 2-channel bases for decomposition -( -z = (0..9); -[z.removeAt(~centroids.minIndex)].do{|chan|FluidBufCompose.process(s, ~bases, startChan: chan, numChans: 1, destination: ~trainedBases, destGain:1)}; -z.postln; -z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, destStartChan: 1, destination: ~trainedBases, destGain:1)}); -) - -~trainedBases.plot; - -//we can then use the resynthesised signal to sent in a delay -( -{ - var source, todelay, delay1, delay2, delay3, feedback, mod1, mod2, mod3, mod4; - //read the source - source = PlayBuf.ar(1, b); - - // generate modulators that are coprime in frequency - mod1 = SinOsc.ar(1, 0, 0.001); - mod2 = SinOsc.ar(((617 * 181) / (461 * 991)), 0, 0.001); - mod3 = SinOsc.ar(((607 * 193) / (491 * 701)), 0, 0.001); - mod4 = SinOsc.ar(((613 * 191) / (463 * 601)), 0, 0.001); - - // compress the signal to send to the delays - todelay = FluidNMFFilter.ar(source,~trainedBases,2,fftSize:2048)[0]; //reading the channel of the activations on the pick basis - - // delay network - feedback = LocalIn.ar(3);// take the feedback in for the delays - delay1 = DelayC.ar(BPF.ar(todelay+feedback[1]+(feedback[2] * 0.3), 987, 6.7,0.8),0.123,0.122+(mod1*mod2)); - delay2 = DelayC.ar(BPF.ar(todelay+feedback[0]+(feedback[2] * 0.3), 1987, 6.7,0.8),0.345,0.344+(mod3*mod4)); - delay3 = DelayC.ar(BPF.ar(todelay+feedback[1], 1456, 6.7,0.8),0.567,0.566+(mod1*mod3),0.6); - LocalOut.ar([delay1,delay2, delay3]); // write the feedback for the delays - - source.dup + ([delay1+delay3,delay2+delay3]*(3.dbamp)) - //listen to the delays in solo by uncommenting the following line - // [delay1+delay3,delay2+delay3] - // [source, todelay] -}.play; -) - -:: -STRONG::Strange Processor:: - CODE:: -//set some buffers -( -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); -c = Buffer.alloc(s,1025,3); -d = Buffer.alloc(s,44100); -) - -// play a source and circular record the last second, continuously -( -e = { var source = PlayBuf.ar(1,b,loop:1); - BufWr.ar(source, d, Phasor.ar(1, end:44100)); - source.dup; -}.play; -) - -// after at least 1 second, trigger a first factorisation -( -Routine { - FluidBufNMF.process(s, d, bases:c, windowSize:2048, components:3); - c.query; -}.play; -) -c.plot - -// wait for the query to print -// then start the splitting effect -( -f = {var source = In.ar(0,2); - ReplaceOut.ar(0, Splay.ar(FluidNMFFilter.ar(source.sum, c, 3, windowSize:2048))); -}.play(addAction:\addToTail); -) - -// kill this boring splitter -f.free; - -// more fun: processing the 3 component independently -( -f = {arg bases = c.bufnum; - var source, x,y,z, rev, dist; - source = In.ar(0,2); - #x,y,z = FluidNMFFilter.ar(source.sum, bases, 3, windowSize:2048); - rev = FreeVerb.ar(x); - dist = (z * 10).atan * 0.1; - ReplaceOut.ar(0, Splay.ar([rev,y,dist])); -}.play(addAction:\addToTail); -) - -// set the bases -f.set(\bases, c.bufnum) - -// here you can retrigger the factorisation -g = Buffer.alloc(s,1025,3); -FluidBufNMF.process(s, d, bases:g, windowSize:2048, components:3); -f.set(\bases, g.bufnum) - -//free -f.free; e.free; b.free; c.free; d.free; g.free; -:: diff --git a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp deleted file mode 100644 index 7af0eb1..0000000 --- a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp +++ /dev/null @@ -1,249 +0,0 @@ -TITLE:: FluidNMFMatch -SUMMARY:: Real-Time Non-Negative Matrix Factorisation with Fixed Bases -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Classes/FluidBufNMF, Classes/FluidNMFFilter - -DESCRIPTION:: -The FluidNMFMatch object matches an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: - -It outputs at kr the degree of detected match for each template (the activation amount, in NMF-terms). The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition (number of components) is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxComponents:: parameter. - -NMF has been a popular technique in signal processing research for things like source separation and transcription footnote:: Smaragdis and Brown, Non-Negative Matrix Factorization for Polyphonic Music Transcription.::, although its creative potential is so far relatively unexplored. It works iteratively, by trying to find a combination of amplitudes ('activations') that yield the original magnitude spectrogram of the audio input when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results. - -The whole process can be related to a channel vocoder where, instead of fixed bandpass filters, we get more complex filter shapes and the activations correspond to channel envelopes. - -FluidNMFMatch is part of the LINK:: Guides/FluidCorpusManipulation::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - -CLASSMETHODS:: - -METHOD:: kr -The real-time processing method. It takes an audio or control input, and will yield a control stream in the form of a multichannel array of size STRONG::maxComponents:: . If the bases buffer has fewer than maxComponents channels, the remaining outputs will be zeroed. - -ARGUMENT:: in -The signal input to the factorisation process. - -ARGUMENT:: bases - The server index of the buffer containing the different bases that the input signal will be matched against. Bases must be STRONG::(fft size / 2) + 1:: frames. If the buffer has more than STRONG::maxComponents:: channels, the excess will be ignored. - -ARGUMENT::maxComponents - The maximum number of elements the NMF algorithm will try to divide the spectrogram of the source in. This dictates the number of output channelsfor the ugen. This cannot be modulated. - -ARGUMENT:: iterations - The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU intensive, lower numbers will be more unpredictable in quality. - -ARGUMENT:: windowSize - The number of samples that are analysed at a time. A lower number yields greater temporal resolution, at the expense of spectral resoultion, and vice-versa. - -ARGUMENT:: hopSize - The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - A multichannel kr output, giving for each basis component the activation amount. - - -EXAMPLES:: -STRONG::A didactic example:: -CODE:: -( -// create buffers -b= Buffer.alloc(s,44100); -c = Buffer.alloc(s, 44100); -d = Buffer.new(s); -e= Buffer.new(s); -) - -( -// fill them with 2 clearly segregated sine waves and composite a buffer where they are consecutive -Routine { - b.sine2([500],[1], false, false); - c.sine2([5000],[1],false, false); - s.sync; - FluidBufCompose.process(s,b, destination:d); - FluidBufCompose.process(s,c, destStartFrame:44100, destination:d, destGain:1); - s.sync; - d.query; -}.play; -) - -// check -d.plot -d.play //////(beware !!!! loud!!!) - -( -// separate them in 2 components -Routine { - FluidBufNMF.process(s, d, bases: e, components:2); - s.sync; - e.query; -}.play -) - -// check for 2 spikes in the spectra -e.plot - -// test the activations values with test one, another, or both ideal material -{FluidNMFMatch.kr(SinOsc.ar(500),e,2)}.plot(1) - -{FluidNMFMatch.kr(SinOsc.ar(5000),e,2)}.plot(1) - -{FluidNMFMatch.kr(SinOsc.ar([500,5000]).sum,e,2)}.plot(1) -:: - -STRONG::A pick compressor:: -CODE:: -//set some buffers -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -c = Buffer.new(s); -~bases = Buffer.new(s); -~spectralshapes = Buffer.new(s); -~stats = Buffer.new(s); -~centroids = Array.new(); -~trainedBases = Buffer.new(s); -) - -// train only 2 seconds -( -Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048).wait; - c.query; -}.play; -) - -// wait for the query to print -// find the component that has the picking sound checking the median spectral centroid -( -FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ - |shapes|FluidBufStats.process(s,shapes,stats:~stats, action:{ - |stats|stats.getn(0, (stats.numChannels * stats.numFrames) ,{ - |x| ~centroids = x.select({ - |item, index| (index.mod(7) == 0) && (index.div(70) == 5); - }) - }) - }) -}); -) - -// we then copy the basis with the highest median centroid to a channel, and all the other bases to the other channel, of a 2-channel bases for decomposition -( -z = (0..9); -[z.removeAt(~centroids.maxIndex)].do{|chan|FluidBufCompose.process(s, ~bases, startChan: chan, numChans: 1, destination: ~trainedBases, destGain:1)}; -z.postln; -z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, destStartChan: 1, destination: ~trainedBases, destGain:1)}); -) - -~trainedBases.plot; - -//using this trained basis we can see the envelop (activations) of each component -{FluidNMFMatch.kr(PlayBuf.ar(1,b),~trainedBases,2,fftSize:2048)}.plot(1); -// the left/top activations are before, the pick before the sustain. - -//we can then use the activation value to sidechain a compression patch that is sent in a delay -( -{ - var source, todelay, delay1, delay2, delay3, feedback, mod1, mod2, mod3, mod4; - //read the source - source = PlayBuf.ar(1, b); - - // generate modulators that are coprime in frequency - mod1 = SinOsc.ar(1, 0, 0.001); - mod2 = SinOsc.ar(((617 * 181) / (461 * 991)), 0, 0.001); - mod3 = SinOsc.ar(((607 * 193) / (491 * 701)), 0, 0.001); - mod4 = SinOsc.ar(((613 * 191) / (463 * 601)), 0, 0.001); - - // compress the signal to send to the delays - todelay = DelayN.ar(source,0.1, 800/44100, //delaying it to compensate for FluidNMFMatch's latency - LagUD.ar(K2A.ar(FluidNMFMatch.kr(source,~trainedBases,2,fftSize:2048)[0]), //reading the channel of the activations on the pick basis - 80/44100, // lag uptime (compressor's attack) - 1000/44100, // lag downtime (compressor's decay) - (1/(2.dbamp) // compressor's threshold inverted - )).clip(1,1000).pow((8.reciprocal)-1)); //clipping it so we only affect above threshold, then ratio(8) becomes the exponent of that base - - // delay network - feedback = LocalIn.ar(3);// take the feedback in for the delays - delay1 = DelayC.ar(BPF.ar(todelay+feedback[1]+(feedback[2] * 0.3), 987, 6.7,0.8),0.123,0.122+(mod1*mod2)); - delay2 = DelayC.ar(BPF.ar(todelay+feedback[0]+(feedback[2] * 0.3), 1987, 6.7,0.8),0.345,0.344+(mod3*mod4)); - delay3 = DelayC.ar(BPF.ar(todelay+feedback[1], 1456, 6.7,0.8),0.567,0.566+(mod1*mod3),0.6); - LocalOut.ar([delay1,delay2, delay3]); // write the feedback for the delays - - source.dup + ([delay1+delay3,delay2+delay3]*(-3.dbamp)) - //listen to the delays in solo by uncommenting the following line - // [delay1+delay3,delay2+delay3] -}.play; -) - -:: - - - STRONG::Strange Resonators:: - CODE:: -//load the source and declare buffers/arrays -( -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav")); -c = Buffer.new(s); -~bases = Buffer.new(s); -~spectralshapes = Buffer.new(s); -~stats = Buffer.new(s); -~centroids = Array.new(); -) - -// train only 2 seconds -( -Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:8, hopSize:256, fftSize:2048).wait; - c.query; -}.play; -) - -// wait for the query to print -// find the component that has the picking sound checking the median spectral centroid -( -FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ - |shapes|FluidBufStats.process(s,shapes,stats:~stats, action:{ - |stats|stats.getn(0, (stats.numChannels * stats.numFrames) ,{ - |x| ~centroids = x.select({ - |item, index| (index.mod(7) == 0) && (index.div(56) == 5); - }) - }) - }) -}); -) - -//the interleave format is 7 stats(frames) x 8 components in the source as tracks x 7 shapes (tracks) -~stats.query -~centroids.size() - -// make a player with harmonic on the out0, percussive on the out1, and 8 ctlout of nmfmatch -~splitaudio = Bus.audio(s,1); -~nmfenvs = Bus.control(s,8); - -// start the player and you should hear only the pick a little -( -x = { - var sound = PlayBuf.ar(1,b,loop:1); - var harm, perc; - # harm, perc = FluidHPSS.ar(sound, maskingMode:1, harmThreshFreq1: 0.005869, harmThreshAmp1: -9.6875, harmThreshFreq2: 0.006609, harmThreshAmp2: -4.375, hopSize:256); - Out.ar(~splitaudio, (harm / 4) + perc); - Out.kr(~nmfenvs, FluidNMFMatch.kr(DelayN.ar(sound,delaytime: ((17-1)*256)/44100), ~bases, maxComponents:8, hopSize:256, fftSize:2048)); - Out.ar(0,perc.dup) -}.play; -) - -// make an array of resonators tuned on the median of the centroids -( -8.do({ - arg i; - { - var audio = BPF.ar(In.ar(~splitaudio,1), ~centroids[i],0.0015,Lag.kr(In.kr(~nmfenvs,8)[i] * 2,0.022)); - Out.ar(0,Pan2.ar(audio, (i / 14) - 0.25)); - }.play(x,addAction: \addAfter); -}); -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidNMFMorph.schelp b/release-packaging/HelpSource/Classes/FluidNMFMorph.schelp deleted file mode 100644 index e11a0a5..0000000 --- a/release-packaging/HelpSource/Classes/FluidNMFMorph.schelp +++ /dev/null @@ -1,93 +0,0 @@ -TITLE:: FluidNMFMorph -summary:: Morph between sounds -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidAudioTransport,Classes/FluidBufNMFCross - - -DESCRIPTION:: -Perform cross-synthesis using Nonnegative Matrix Factorization (NMF) and Optimal Transport -(OT). NMF analyses of code::source:: and code::target:: sounds decompose their material in to a selectable number of components, which are in turn represented by their emphasis::bases:: (spectrum) and emphasis::activations:: (temporal pattern of each component). - -code::FluidNMFMorph:: provides the ability to interpolate between code::source:: and code::target:: spectra using a technique called Optimal Transport, that provides richer results than a simple linear interpolation between spectral shapes. The resulting sound is built up using a buffer of temporal activations, then resynthesised using a phase estimate. - -CLASSMETHODS:: - -METHOD:: ar -Given buffers of spectral and temporal data from a NMF anlaysis such as produced by link::Classes/FluidBufNMF::, cross-synthesise a hybrid sound. - -ARGUMENT:: source -A link::Classes/Buffer:: with the spectral bases for the source sound. - -ARGUMENT:: target -A link::Classes/Buffer:: with the spectral bases for the target sound. - -ARGUMENT:: activations -A link::Classes/Buffer:: with the temporal activations for the target sound. - -ARGUMENT:: autoassign -If set to code::1:: the algorithm will attempt to optimally match which NMF basis components from source and target best match each other, and will use this mapping as its basis for interpolation. warning::changing this value re-initalizes the process:: - -ARGUMENT:: interp -Set the relative contributions of code::source:: and code::target:: between 0 and 1. - -ARGUMENT:: windowSize -The analysis window size in samples. Needs to match that of the seeding NMF analyses - -ARGUMENT:: hopSize -The analysis hop size in samples. Needs to match that of the seeding NMF analyses - -ARGUMENT:: fftSize -The analysis FFT size in samples. Needs to match that of the seeding NMF analyses - -ARGUMENT:: maxFFTSize -The maximum FFT size to allocate memory for - -INSTANCEMETHODS:: - -private:: checkInputs, init - -EXAMPLES:: - -code::FluidNMFMorph:: relies on preexisting NMF analyses to generate variations between sounds. We can produce these using link::Classes/FluidBufNMF:: - -code:: -//read some audio -( -~src1 = Buffer.readChannel(s,FluidFilesPath("Nicol-LoopE-M.wav"),channels:[0]); //some drums -~src2 = Buffer.readChannel(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"),channels:[0]);//some piano - -~src1Bases = Buffer.new; -~src2Bases = Buffer.new; -~src1Activations = Buffer.new; -~src2Activations = Buffer.new; -) -//nmf analyses -( -FluidBufNMF.process(s,~src1,bases:~src1Bases,activations:~src1Activations,components:5, action:{"Analysed Source 1".postln}); -FluidBufNMF.process(s,~src2,bases:~src2Bases,activations:~src2Activations, components:5, action:{"Analysed Source 2".postln}); -) - -( -~morph = { |source, target, activations, interp, autoassign| - FluidNMFMorph.ar(source,target,activations,autoassign,interp) * 80 -}; -) - -~synth = ~morph.play(s,args:[\source,~src1Bases,\target,~src2Bases,\activations,~src2Activations,\interp,0.5,\autoassign,1]); - -//Play with different interpolation values -~synth.set(\interp,0.0); -~synth.set(\interp,1.0); -:: -warning::The following parameters current require one to change the 'autoassign' control to update the process:: -code:: -//Change the actvations -~synth.set(\activations, ~src1Activations, \autoassign,0); -~synth.set(\autoassign,1); -~synth.set(\activations, ~src2Activations, \autoassign,0); -~synth.set(\autoassign,1); - -//Swap source and target -~synth.set(\source,~src2Bases,\target,~src1Bases, \autoassign,0); -~synth.set(\autoassign,1); -:: diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp deleted file mode 100644 index b348e51..0000000 --- a/release-packaging/HelpSource/Classes/FluidNormalize.schelp +++ /dev/null @@ -1,156 +0,0 @@ -TITLE:: FluidNormalize -summary:: Normalize a FluidDataSet -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidStandardize, Classes/FluidRobustScale, Classes/FluidDataSet - -DESCRIPTION:: -Normalize the entries of a link::Classes/FluidDataSet::, or normalize a data point according to the learned bounds of a data set. On the server. - -See http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html - -CLASSMETHODS:: - -private:: kr - -METHOD:: new -Create a new instance -ARGUMENT:: server -The link::Classes/Server:: on which to run -ARGUMENT:: min -Minimum output value, default 0 -ARGUMENT:: max -Maximum output value, default 1 -ARGUMENT:: invert -The direction in which the normalization will occur for transform and transformpoint. The default 0 is taking in the range of the input used to fit and transforms it towards the normalised range. A value of 1 will expect an input of the normalized range to transform back to the original range. - - -INSTANCEMETHODS:: - -METHOD:: fit -Compute the normalization factors from a link::Classes/FluidDataSet:: for later. -ARGUMENT:: dataSet -The link::Classes/FluidDataSet:: to normalize -ARGUMENT:: action -A function to run when processing is complete - -METHOD:: transform -Normalize a link::Classes/FluidDataSet:: into another link::Classes/FluidDataSet::, using the learned extrema from a previous call to link::Classes/FluidNormalize#fit:: -ARGUMENT:: sourceDataSet -The link::Classes/FluidDataSet:: to normalize -ARGUMENT:: destDataSet -The link::Classes/FluidDataSet:: to populate with normalized data -ARGUMENT:: action -A function to run when processing is complete - - -METHOD:: fitTransform -Normalize a link::Classes/FluidDataSet:: -ARGUMENT:: sourceDataSet -The link::Classes/FluidDataSet:: to normalize -ARGUMENT:: destDataSet -The link::Classes/FluidDataSet:: to populate with normalized data -ARGUMENT:: action -A function to run when processing is complete - - -METHOD:: transformPoint -Normalize a new data point, using the learned extrema from a previous call to link::Classes/FluidNormalize#fit:: -ARGUMENT:: sourceBuffer -A link::Classes/Buffer:: with the new data point -ARGUMENT:: destBuffer -A link::Classes/Buffer:: to contain the normalized value -ARGUMENT:: action -A function to run when processing is complete - -EXAMPLES:: -code:: -s.boot; -//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidNormalize -// FluidNormalize.dumpAllMethods -( -~audiofile = FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav"); -~raw = FluidDataSet(s); -~norm = FluidDataSet(s); -~pitch_feature = Buffer.new(s); -~stats = Buffer.alloc(s, 7, 2); -~normalizer = FluidNormalize(s); -) - -// Load audio and run a pitch analysis, which gives us pitch and pitch confidence (so a 2D datum) -( -~audio = Buffer.read(s,~audiofile); -FluidBufPitch.process(s,~audio, features: ~pitch_feature); -) - -// Divide the time series in to 10, and take the mean of each segment and add this as a point to -// the 'raw' FluidDataSet -( -{ - var trig = LocalIn.kr(1, 1); - var buf = LocalBuf(2, 1); - var count = PulseCount.kr(trig) - 1; - var chunkLen = (~pitch_feature.numFrames / 10).asInteger; - var stats = FluidBufStats.kr( - source: ~pitch_feature, startFrame: count * chunkLen, - numFrames: chunkLen, stats: ~stats, trig: (trig * (count <=9)), blocking:1 - ); - 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, idNumber: count, trig: Done.kr(stats)); - LocalOut.kr( Done.kr(dsWr)); - Poll.kr(trig,count,\count); - FreeSelf.kr(count - 9); -}.play; -) - -// Normalize and load to language-side array -( -~rawarray = Array.new(10); -~normedarray= Array.new(10); -~normalizer.fitTransform(~raw,~norm, { - ~raw.dump{|x| 10.do{|i| - ~rawarray.add(x["data"][i.asString]) - }}; - ~norm.dump{|x| 10.do{|i| - ~normedarray.add(x["data"][i.asString]) - }}; -}); -) - -//Plot side by side. Before normalization the two dimensions have radically different scales -//which can be unhelpful in many cases - -( -(~rawarray ++ 0).flop.plot("Unnormalized",Rect(0,0,400,400),minval:0,maxval:[5000,1]).plotMode=\bars; -(~normedarray ++ 0).flop.plot("Normalized",Rect(410,0,400,400)).plotMode=\bars; -) - -// single point transform on arbitrary value -~inbuf = Buffer.loadCollection(s,0.5.dup); -~outbuf = Buffer.new(s); -~normalizer.transformPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,2,{|y|y.postln;};)}); - -//Server side queries -( -{ - var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); - var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:99); - var trig = A2K.kr(HPZ1.ar(counter) < 0); - //average 100 frames: one could use the MovingAverage extension here - var avg; - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(2); - var avgBuf = LocalBuf(100,2); - //running average of pitch features - BufWr.kr(FluidPitch.kr(audio),avgBuf,phase:counter); - avg = Mix.new(BufRd.kr(2, avgBuf, phase:100.collect{|x|x})) * 0.01; - //assemble data point - BufWr.kr(avg[0],inputPoint,0); - BufWr.kr(avg[1],inputPoint,1); - ~normalizer.kr(trig,inputPoint,outputPoint); - Poll.kr(trig,BufRd.kr(1,inputPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); - Poll.kr(trig,BufRd.kr(1,outputPoint,[0,1]),["pitch (normalized)", "confidence (normalized)"]) -}.play; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidNoveltySlice.schelp deleted file mode 100644 index 5ef879e..0000000 --- a/release-packaging/HelpSource/Classes/FluidNoveltySlice.schelp +++ /dev/null @@ -1,85 +0,0 @@ -TITLE:: FluidNoveltySlice -SUMMARY:: Real-Time Novelty-Based Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -DESCRIPTION:: -This class implements a real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. - - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: feature - The feature on which novelty is computed. - table:: - ##0 || Spectrum || The magnitude of the full spectrum. - ##1 || MFCC || 13 Mel-Frequency Cepstrum Coefficients. - ##2 || Chroma || The contour of a 12 band Chromagram. - ##3 || Pitch || The pitch and its confidence. - ##4 || Loudness || The TruePeak and Loudness. -:: -ARGUMENT:: kernelSize - The granularity of the window in which the algorithm looks for change, in samples. A small number will be sensitive to short term changes, and a large number should look for long term changes. - -ARGUMENT:: threshold - The normalised threshold, between 0 an 1, on the novelty curve to consider it a segmentation point. - -ARGUMENT:: filterSize - The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes. - -ARGUMENT:: minSliceLength - The minimum duration of a slice in number of hopSize. - -ARGUMENT:: windowSize - The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -ARGUMENT:: maxKernelSize - This cannot be modulated. - -ARGUMENT:: maxFilterSize - This cannot be modulated. - -RETURNS:: -An audio stream with impulses at detected transients. The latency between the input and the output is STRONG::hopSize * (((kernelSize+1)/2).asInteger + ((filterSize + 1) / 2).asInteger + 1):: samples at minimum. - -EXAMPLES:: - -code:: -//load some sounds -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); - -// basic param (the process add a latency of windowSize samples -{var sig = PlayBuf.ar(1,b,loop:1); [FluidNoveltySlice.ar(sig,0,11,0.33) * 0.5, DelayN.ar(sig, 1, (512 * (((11 + 1) / 2).asInteger + ((1 + 1) / 2).asInteger + 1)) / s.sampleRate, 0.2)]}.play - -// other parameters -{var sig = PlayBuf.ar(1,b,loop:1); [FluidNoveltySlice.ar(sig, 1, 31, 0.0035, 4, 100, 128, 32) * 0.5, DelayN.ar(sig, 1, (32 * (((31 + 1)/2).asInteger + ((4 + 1) / 2).asInteger + 1))/ s.sampleRate,0.2)]}.play - -// More musical, novelty-trigged autopan -( -{ - var sig, trig, syncd, pan; - sig = PlayBuf.ar(1,b,loop:1); - trig = FluidNoveltySlice.ar(sig, 0, 11, 0.25, 5, 1, 128, 32); - syncd = DelayN.ar(sig, 1, (32 * (((11 + 1)/2).asInteger + ((5 + 1) / 2).asInteger + 1))/ s.sampleRate); - pan = TRand.ar(-1,1,trig); - Pan2.ar(syncd,pan); -}.play -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp deleted file mode 100644 index da77a5a..0000000 --- a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp +++ /dev/null @@ -1,85 +0,0 @@ -TITLE:: FluidOnsetSlice -SUMMARY:: Spectral Difference-Based Real-Time Audio Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -DESCRIPTION:: -This class implements many spectral based onset detection metrics, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. - - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: metric - The metric used to derive a difference curve between spectral frames. It can be any of the following: - TABLE:: - ##0 || Energy || thresholds on (sum of squares of magnitudes / nBins) (like Onsets \power) - ##1 || HFC || thresholds on (sum of (squared magnitudes * binNum) / nBins) - ##2 || SpectralFlux || thresholds on (diffence in magnitude between consecutive frames, half rectified) - ##3 || MKL || thresholds on (sum of log of magnitude ratio per bin) (or equivalent: sum of difference of the log magnitude per bin) (like Onsets \mkl) - ##4 || IS || (WILL PROBABLY BE REMOVED) Itakura - Saito divergence (see literature) - ##5 || Cosine || thresholds on (cosine distance between comparison frames) - ##6 || PhaseDev || takes the past 2 frames, projects to the current, as anticipated if it was a steady state, then compute the sum of the differences, on which it thresholds (like Onsets \phase) - ##7 || WPhaseDev || same as PhaseDev, but weighted by the magnitude in order to remove chaos noise floor (like Onsets \wphase) - ##8 || ComplexDev || same as PhaseDev, but in the complex domain - the anticipated amp is considered steady, and the phase is projected, then a complex subtraction is done with the actual present frame. The sum of magnitudes is used to threshold (like Onsets \complex) - ##9 || RComplexDev || same as above, but rectified (like Onsets \rcomplex) - :: - -ARGUMENT:: threshold -The thresholding of a new slice. Value ranges are different for each metric, from 0 upwards. - -ARGUMENT:: minSliceLength - The minimum duration of a slice in number of hopSize. - -ARGUMENT:: filterSize - The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes. - -ARGUMENT:: frameDelta - For certain metrics (HFC, SpectralFlux, MKL, Cosine), the distance does not have to be computed between consecutive frames. By default (0) it is, otherwise this sets the distane between the comparison window in samples. - -ARGUMENT:: windowSize - The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - An audio stream with impulses at detected transients. The latency between the input and the output is windowSize at maximum. - -EXAMPLES:: - -code:: -//load some sounds -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); - -// basic param (the process add a latency of windowSize samples -{var sig = PlayBuf.ar(1,b,loop:1); [FluidOnsetSlice.ar(sig) * 0.5, DelayN.ar(sig, 1, 1024/ s.sampleRate)]}.play - -// other parameters -{var sig = PlayBuf.ar(1,b,loop:1); [FluidOnsetSlice.ar(sig, 9, 0.15, 45, 9, 0, 128, 64) * 0.5, DelayN.ar(sig, 1, (128)/ s.sampleRate)]}.play - -// More musical, onset-trigged autopan -( -{ - var sig, trig, syncd, pan; - sig = PlayBuf.ar(1,b,loop:1); - trig = FluidOnsetSlice.ar(sig, 7, 0.2, 100, 9, 0, 128); - syncd = DelayN.ar(sig, 1, (128 / s.sampleRate)); //one could add a bit more delay here to make sure to catch the attack - pan = Stepper.ar(trig,-1,-1,1,1); - Pan2.ar(syncd,pan); -}.play -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp deleted file mode 100644 index d38edf0..0000000 --- a/release-packaging/HelpSource/Classes/FluidPCA.schelp +++ /dev/null @@ -1,169 +0,0 @@ -TITLE:: FluidPCA -summary:: Dimensionality Reduction with Principal Component Analysis -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidMDS, Classes/FluidDataSet - -DESCRIPTION:: - -Principal Components Analysis of a link::Classes/FluidDataSet:: - -https://scikit-learn.org/stable/modules/decomposition.html#principal-component-analysis-pca - -CLASSMETHODS:: - -METHOD:: new -Make a new instance -ARGUMENT:: server -The server on which to run this model -ARGUMENT:: numDimensions -The number of dimensions to reduce to - -INSTANCEMETHODS:: - -PRIVATE:: init - -METHOD:: fit -Train this model on a link::Classes/FluidDataSet:: but don't transform the data -ARGUMENT:: dataSet -A link::Classes/FluidDataSet:: to analyse -ARGUMENT:: action -Run when done - -METHOD:: transform -Given a trained model, apply the reduction to a source link::Classes/FluidDataSet:: and write to a destination. Can be the same for both (in-place) -ARGUMENT:: sourceDataSet -Source data, or the DataSet name -ARGUMENT:: destDataSet -Destination data, or the DataSet name -ARGUMENT:: action -Run when done. The fraction of accounted variance is passed as an argument, aka the fidelity of the new representation: a value near 1.0 means a higher fidelity to the original. - -METHOD:: fitTransform -link::Classes/FluidPCA#fit:: and link::Classes/FluidPCA#transform:: in a single pass -ARGUMENT:: sourceDataSet -Source data, or the DataSet name -ARGUMENT:: destDataSet -Destination data, or the DataSet name -ARGUMENT:: action -Run when done. The fraction of accounted variance is passed as an argument, aka the fidelity of the new representation: a value near 1.0 means a higher fidelity to the original. - -METHOD:: transformPoint -Given a trained model, transform the data point in a link::Classes/Buffer:: and write to an output -ARGUMENT:: sourceBuffer -Input data -ARGUMENT:: destBuffer -Output data -ARGUMENT:: action -Run when done. The function is passed code::destBuffer:: as argument. - -EXAMPLES:: - -code:: -s.reboot; -//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidPCA -( -~audiofile = FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav"); -~raw = FluidDataSet(s); -~standardized = FluidDataSet(s); -~reduced = FluidDataSet(s); -~audio = Buffer.read(s,~audiofile); -~mfcc_feature = Buffer.new(s); -~stats = Buffer.alloc(s, 7, 12); -~datapoint = Buffer.alloc(s, 12); -~standardizer = FluidStandardize(s); -~pca = FluidPCA(s,2); -) - - -// Load audio and run an mfcc analysis, which gives us 13 points (we'll throw the 0th away) -( -~audio = Buffer.read(s,~audiofile); -FluidBufMFCC.process(s,~audio, features: ~mfcc_feature,action:{"Done MFCCs".postln}); -) - -// Divide the time series in 100, and take the mean of each segment and add this as a point to -// the 'raw' FluidDataSet -( -{ - var trig = LocalIn.kr(1, 1); - var buf = LocalBuf(12, 1); - var count = PulseCount.kr(trig) - 1; - 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 * (count < 100), 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, idNumber: count, trig: Done.kr(stats)); - LocalOut.kr( Done.kr(dsWr)); - FreeSelf.kr(count - 99); - Poll.kr(trig,(100 - count)); -}.play; -) -// wait for the count to reaches 0 in the post window. - -//First standardize our DataSet, so that the MFCC dimensions are on comensurate scales -//Then apply the PCA in-place on the standardized data -//Download the DataSet contents into an array for plotting -( -~reducedarray = Array.new(100); -~standardizer.fitTransform(~raw, ~standardized); -~pca.fitTransform(~standardized, ~reduced, action:{|x| - x.postln; //pass on the variance - ~reduced.dump{|x| 100.do{|i| - ~reducedarray.add(x["data"][i.asString]) - }}; -}); -) - -//Visualise the 2D projection of our original 12D data -( -d = ~reducedarray.flop.deepCollect(1, { |x| x.normalize}); -w = Window("scatter", Rect(128, 64, 200, 200)); -w.drawFunc = { - Pen.use { - d[0].size.do{|i| - var x = (d[0][i]*200); - var y = (d[1][i]*200); - var r = Rect(x,y,5,5); - Pen.fillColor = Color.blue; - Pen.fillOval(r); - } - } -}; -w.refresh; -w.front; -) - -// transform a single point with arbitrary value -~inbuf = Buffer.loadCollection(s,0.5.dup(12)); -~outbuf = Buffer.new(s); -~pca.transformPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,1,{|y|y.postln;};)}); -:: - -subsection:: Server Side Queries - -Let's map our learned PCA dimensions to the controls of a processor - -code:: -( -{ - var mapped; - var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); - var mfcc = FluidMFCC.kr(audio)[1..12]; - var smoothed = LagUD.kr(mfcc,1*ControlDur.ir,500*ControlDur.ir); - var trig = Impulse.kr(ControlRate.ir / 2); - var inputPoint = LocalBuf(12); - var outputPoint = LocalBuf(2); - smoothed.collect{|coeff,i| BufWr.kr([coeff],inputPoint,i)}; - ~pca.kr(trig, inputPoint, outputPoint, 2); - mapped = BufRd.kr(1,outputPoint, phase:[0,1]).linlin(-3,3,0,3); - CombC.ar(audio,3,mapped[0],mapped[1]*3) -}.play; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidPitch.schelp b/release-packaging/HelpSource/Classes/FluidPitch.schelp deleted file mode 100644 index 36a2c28..0000000 --- a/release-packaging/HelpSource/Classes/FluidPitch.schelp +++ /dev/null @@ -1,164 +0,0 @@ -TITLE:: FluidPitch -SUMMARY:: A Selection of Pitch Descriptors in Real-Time -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Classes/Pitch - -DESCRIPTION:: -FluidPitch implements three popular pitch descriptors. It outputs two values: the computed frequency and the confidence in the accuracy of that frequency. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning materials, and discussions on its musicianly uses, visit https://www.flucoma.org/reference/pitch - -FluidPitch outputs a multichannel control steam of [pitch, confidence] values. The 'unit' argument changes the unit of the pitch output. 'unit' set to 0 will return a frequency in Hz, 'unit' set to 1 will return the MIDI note (with a decimal for microtonal values). If the chosen algorithm cannot determine a fundamental pitch at all, a frequency of 0 Hz is returned (or -999.0 when the unit is in MIDI note). - -When the hopSize is larger than the server's kr period, the returned values will be repeated until the next window is computed. - -CLASSMETHODS:: - -METHOD:: kr - The audio rate in, control rate out - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: algorithm - The algorithm to estimate the pitch. The options are: - TABLE:: - ## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC). - ## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 :: - ## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications.” QMUL, London, UK, 2007. :: See also FOOTNOTE::https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html:: - :: - -ARGUMENT:: minFreq -The minimum frequency that the algorithm will search for an estimated fundamental. This sets the lowest value that will be generated. The default is 20. - -ARGUMENT:: maxFreq -The maximum frequency that the algorithm will search for an estimated fundamental. This sets the highest value that will be generated. The default is 10000. - -ARGUMENT:: unit -The unit of the estimated frequency. The default of 0 is in Hz. A value of 1 will convert to MIDI note values. - -ARGUMENT:: windowSize - The window size. The number of samples used to calculate the FFT. - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It must 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 to or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is windowSize. - - -EXAMPLES:: - - -code:: -//create a monitoring bus for the descriptors -b = Bus.new(\control,0,4); - -//create a monitoring window for the values -( -w = Window("Frequency Monitor", Rect(10, 10, 220, 115)).front; - -c = Array.fill(4, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("FluidPitch: "); -c[1].string = ("confidence: "); -c[2].string = ("SC Pitch: "); -c[3].string = ("Confidence: "); - -a = Array.fill(4, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); -) - -//routine to update the parameters -( -r = Routine { - { - - b.get({ arg val; - { - if(w.isClosed.not) { - val.do({arg item,index; - a[index].string = item.round(0.01)}) - } - }.defer - }); - - 0.1.wait; - }.loop - -}.play -) - -//test signals, all in one synth -( -x = { - arg freq=220, type = 0, noise = 0; - var source = PinkNoise.ar(noise) + Select.ar(type,[SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1), Mix.new(Array.fill(8, {arg i; SinOsc.ar(LFNoise1.kr(0.1.rand,10,220*(i+1)),mul:(i+1).reciprocal * 0.1)}))]); - Out.kr(b, FluidPitch.kr(source) ++ Pitch.kr(source)); - source.dup; -}.play; -) - -// the built-in is slightly better on pure sinewaves -x.set(\freq, 440) - -// adding harmonics, by changing to triangle (1), saw (2) or square (3) shows that spectral algo are more resilient when signal are richer -x.set(\type, 1) -x.set(\type, 2) -x.set(\type, 3) - -// adding noise shows the comparative sturdiness of the spectral pitch tracker -x.set(\noise, 0.05) - -//if latency is no issue, getting a higher windowSize will stabilise the algorithm even more -:: - -STRONG::a more musical example:: -CODE:: -// play a noisy synth file -b = Buffer.read(s,FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav")); -b.play(true); - -//insert a selective reverb - sending only the material with very high pitch confidence -( -f = {var source, rev; - source = In.ar(0,2); - rev = FreeVerb.ar(DelayN.ar(source,delaytime:1024/s.sampleRate) * Lag.kr((FluidPitch.kr(source.sum)[1] > 0.98),0.01), 1); - ReplaceOut.ar(0, rev+ source); -}.play(addAction:\addToTail); -) - -// free the effect -f.free - -// insert a stereo delay instead (as well) using the same -( -f = { - var source, todelay, delay1, delay2, delay3, feedback, mod1, mod2, mod3, mod4; - //read the source - source = In.ar(0,2); - - // generate modulators that are coprime in frequency - mod1 = SinOsc.ar(1, 0, 0.001); - mod2 = SinOsc.ar(((617 * 181) / (461 * 991)), 0, 0.001); - mod3 = SinOsc.ar(((607 * 193) / (491 * 701)), 0, 0.001); - mod4 = SinOsc.ar(((613 * 191) / (463 * 601)), 0, 0.001); - - // gate the signal to send to the delays - todelay = DelayN.ar(source,delaytime:1024/s.sampleRate) * Lag.kr((FluidPitch.kr(source.sum)[1] > 0.98),0.01); - - // delay network - feedback = LocalIn.ar(3);// take the feedback in for the delays - delay1 = DelayC.ar(BPF.ar(todelay+feedback[1]+(feedback[2] * 0.3), 987, 6.7,0.35),0.123,0.122+(mod1*mod2)); - delay2 = DelayC.ar(BPF.ar(todelay+feedback[0]+(feedback[2] * 0.3), 1987, 6.7,0.35),0.345,0.344+(mod3*mod4)); - delay3 = DelayC.ar(BPF.ar(todelay+feedback[1], 1456, 6.7,0.35),0.567,0.566+(mod1*mod3),0.6); - LocalOut.ar([delay1,delay2, delay3]); // write the feedback for the delays - - ReplaceOut.ar(0, source + [delay1+delay3,delay2+delay3]); -}.play(addAction:\addToTail); -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidRobustScale.schelp b/release-packaging/HelpSource/Classes/FluidRobustScale.schelp deleted file mode 100644 index fd565b8..0000000 --- a/release-packaging/HelpSource/Classes/FluidRobustScale.schelp +++ /dev/null @@ -1,158 +0,0 @@ -TITLE:: FluidRobustScale -summary:: Apply Robust Scaling to FluidDataSet -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidStandardize, Classes/FluidNormalize, Classes/FluidDataSet - -DESCRIPTION:: -Scales the entries of a link::Classes/FluidDataSet::, or scales a data point according to the learned values of a data set. It will centre the median of each dimension to 0, and will scale the data to +/- the provided centiles, by default providing the first and third qartile (25 and 75).All happens on the server. - -See https://scikit-learn.org/stable/auto_examples/preprocessing/plot_all_scaling.html#robustscaler - -CLASSMETHODS:: - -private:: kr - -METHOD:: new -Create a new instance -ARGUMENT:: server -The link::Classes/Server:: on which to run -ARGUMENT:: low -The low centile boundary, default 25. -ARGUMENT:: high -The high centile boundary, default 75. -ARGUMENT:: invert -The direction in which the scaling will occur for transform and transformpoint. The default 0 is taking in the range of the input used to fit and transforms it towards the robust scaling range. A value of 1 will expect an input of the scaling range to transform back to the original range. - - -INSTANCEMETHODS:: - -METHOD:: fit -Compute the scaling factors from a link::Classes/FluidDataSet:: for later. -ARGUMENT:: dataSet -The link::Classes/FluidDataSet:: to scale -ARGUMENT:: action -A function to run when processing is complete - -METHOD:: transform -Scale a link::Classes/FluidDataSet:: into another link::Classes/FluidDataSet::, using the learned range from a previous call to link::Classes/FluidRobustScale#fit:: -ARGUMENT:: sourceDataSet -The link::Classes/FluidDataSet:: to scale -ARGUMENT:: destDataSet -The link::Classes/FluidDataSet:: to populate with scaled data -ARGUMENT:: action -A function to run when processing is complete - - -METHOD:: fitTransform -Scale a link::Classes/FluidDataSet:: -ARGUMENT:: sourceDataSet -The link::Classes/FluidDataSet:: to scale -ARGUMENT:: destDataSet -The link::Classes/FluidDataSet:: to populate with scaled data -ARGUMENT:: action -A function to run when processing is complete - - -METHOD:: transformPoint -Scale a new data point, using the learned scaling from a previous call to link::Classes/FluidRobustScale#fit:: -ARGUMENT:: sourceBuffer -A link::Classes/Buffer:: with the new data point -ARGUMENT:: destBuffer -A link::Classes/Buffer:: to contain the scaled value -ARGUMENT:: action -A function to run when processing is complete - -EXAMPLES:: -code:: -s.boot; -//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidRobustScale -( -~audiofile = FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav"); -~raw = FluidDataSet(s); -~scaled = FluidDataSet(s); -~pitch_feature = Buffer.new(s); -~stats = Buffer.alloc(s, 7, 2); -~robust = FluidRobustScale(s); -) - -// Load audio and run a pitch analysis, which gives us pitch and pitch confidence (so a 2D datum) -( -~audio = Buffer.read(s,~audiofile); -FluidBufPitch.process(s,~audio, features: ~pitch_feature); -) - -// Divide the time series in to 10, and take the mean of each segment and add this as a point to -// the 'raw' FluidDataSet -( -{ - var trig = LocalIn.kr(1, 1); - var buf = LocalBuf(2, 1); - var count = PulseCount.kr(trig) - 1; - var chunkLen = (~pitch_feature.numFrames / 10).asInteger; - var stats = FluidBufStats.kr( - source: ~pitch_feature, startFrame: count * chunkLen, - numFrames: chunkLen, stats: ~stats, trig: (trig * (count <=9)), blocking:1 - ); - 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, idNumber: count, trig: Done.kr(stats)); - LocalOut.kr( Done.kr(dsWr)); - Poll.kr(trig,count,\count); - FreeSelf.kr(count - 9); -}.play; -) - -//check the dataset -~raw.print; - -// Scale and load to language-side array -( -~rawarray = Array.new(10); -~scaledarray= Array.new(10); -~robust.fitTransform(~raw,~scaled, { - ~raw.dump{|x| 10.do{|i| - ~rawarray.add(x["data"][i.asString]) - }}; - ~scaled.dump{|x| 10.do{|i| - ~scaledarray.add(x["data"][i.asString]) - }}; -}); -) - -//Plot side by side. Before normalization the two dimensions have radically different scales -//which can be unhelpful in many cases - -( -(~rawarray ++ 0).flop.plot("Raw Data",Rect(0,0,400,400),minval:0,maxval:[5000,1]).plotMode=\bars; -(~scaledarray ++ 0).flop.plot("Scaled",Rect(410,0,400,400), minval:-2,maxval:2).plotMode=\bars; -) - -// single point transform on arbitrary value -~inbuf = Buffer.loadCollection(s,0.5.dup); -~outbuf = Buffer.new(s); -~robust.transformPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,2,{|y|y.postln;};)}); - -//Server side queries -( -{ - var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); - var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:99); - var trig = A2K.kr(HPZ1.ar(counter) < 0); - //average 100 frames: one could use the MovingAverage extension here - var avg; - var inputPoint = LocalBuf(2); - var outputPoint = LocalBuf(2); - var avgBuf = LocalBuf(100,2); - //running average of pitch features - BufWr.kr(FluidPitch.kr(audio),avgBuf,phase:counter); - avg = Mix.new(BufRd.kr(2, avgBuf, phase:100.collect{|x|x})) * 0.01; - //assemble data point - BufWr.kr(avg[0],inputPoint,0); - BufWr.kr(avg[1],inputPoint,1); - ~robust.kr(trig,inputPoint,outputPoint); - Poll.kr(trig,BufRd.kr(1,inputPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); - Poll.kr(trig,BufRd.kr(1,outputPoint,[0,1]),["pitch (scaled)", "confidence (scaled)"]) -}.play; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp b/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp deleted file mode 100644 index c8f53f4..0000000 --- a/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp +++ /dev/null @@ -1,61 +0,0 @@ -TITLE:: FluidSTFTPass -SUMMARY:: Real-Time FFT/IFFT return trip. -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation,Classes/UnaryOpFunction - - -DESCRIPTION:: -This class implements a sanity test for the FluCoMa Real-Time Client FFT/IFFT Wrapper. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The input to be passed-through - -ARGUMENT:: windowSize - The size of the buffered window to be analysed, in samples. It will add that much latency to the signal. - -ARGUMENT:: hopSize - How much the buffered window moves forward, in samples. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - How large will the FFT be, zero-padding the buffer to the right size, which should be bigger than the windowSize argument, bigger than 4 samples, and should be a power of 2. This is a way to oversample the FFT for extra precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - - -RETURNS:: - Same as input, delayed by the windowSize. - - -EXAMPLES:: - -Summing with the inverse (gain of -1) with a delay of the latency gives us CPU-expensive silence. -CODE:: -{ var source = PinkNoise.ar(0.1); DelayN.ar(source, delaytime:1024/s.sampleRate, mul: -1) + FluidSTFTPass.ar(source, 1024); }.play -:: -Larger, oversampled, FFT -CODE:: -{ FluidSTFTPass.ar(PinkNoise.ar(0.1), 2048, 128, 8192) }.play -:: -Stereo Input Tests. -CODE:: -{ FluidSTFTPass.ar([SinOsc.ar(222,mul: 0.1), PinkNoise.ar(Decay.ar(Impulse.ar(0.666,mul: 0.2), 0.5))], fftSize:1024)}.play -:: -Stereo Parameter Tests. -CODE:: -{ FluidSTFTPass.ar(SinOsc.ar(222,mul: 0.1), [1024,8192],256,8192)}.play -:: -Modulating Window Param Tests. -CODE:: -{ var source = SinOsc.ar(222,mul: 0.1); [source, FluidSTFTPass.ar(source,LFNoise0.kr(1).range(10,10000))] }.play -:: -Very Short FFT test. -CODE:: -{ var source = SinOsc.ar(222,mul: 0.1); [source, FluidSTFTPass.ar(source,10)] }.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidSines.schelp b/release-packaging/HelpSource/Classes/FluidSines.schelp deleted file mode 100644 index e37e828..0000000 --- a/release-packaging/HelpSource/Classes/FluidSines.schelp +++ /dev/null @@ -1,93 +0,0 @@ -TITLE:: FluidSines -SUMMARY:: Sinusoidal Modelling and Resynthesis -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -DESCRIPTION:: -This class applies a Sinusoidal Modelling process on its audio input. It implements a mix of algorithms taken from classic papers. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - The algorithm will take an audio in, and will divide it in two parts: LIST:: - ## a reconstruction of what it detects as sinusoidal; - ## a residual derived from the previous signal to allow null-summing:: - - The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. - - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The input to be processed - -ARGUMENT:: bandwidth - The number of bins used to resynthesises a peak. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. It is capped at (fftSize / 2) + 1. - -ARGUMENT:: detectionThreshold - The threshold in dB above which a magnitude peak is considered to be a sinusoidal component. - -ARGUMENT:: birthLowThreshold - The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the low end of the spectrum. It is interpolated across the spectrum until birthHighThreshold at half-Nyquist. - -ARGUMENT:: birthHighThreshold - The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the high end of the spectrum. It is interpolated across the spectrum until birthLowThreshold at DC. - -ARGUMENT:: minTrackLen - The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefacts, but is more CPU intensive and might reject quick pitch material. - -ARGUMENT:: trackingMethod - The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive "Hungarian" one. footnote::Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018.:: - -ARGUMENT:: trackMagRange - The amplitude difference allowed for a track to diverge between frames, in dB. - -ARGUMENT:: trackFreqRange - The frequency difference allowed for a track to diverge between frames, in Hertz. - -ARGUMENT:: trackProb - The propensity of peaks to become enrolled into tracks. - -ARGUMENT:: windowSize - The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to the next power of 2 equal or above the highest of the windowSize and (bandwidth - 1) * 2. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - An array of two audio streams: [0] is the harmonic part extracted, [1] is the rest. The latency between the input and the output is (( hopSize * minTrackLen) + windowSize) samples. - - -EXAMPLES:: - -CODE:: -// load some audio to play -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); - -// run with large parameters - left is sinusoidal model, right is residual -{FluidSines.ar(PlayBuf.ar(1,b,loop:1),detectionThreshold: -40, minTrackLen: 2, windowSize: 2048, fftSize: 8192)}.play - -// interactive parameters with a narrower bandwidth -{FluidSines.ar(PlayBuf.ar(1,b,loop:1), 30, MouseX.kr(-140,-10),MouseY.kr(-110,-10),MouseY.kr(-140,-40), 10 , windowSize: 1000, hopSize: 200, fftSize: 4096)}.play - -// null test (the process add a latency of (( hopSize * minTrackLen) + windowSize) samples -{var sig = PlayBuf.ar(1,b,loop:1); [FluidSines.ar(sig).sum - DelayN.ar(sig, 1, ((( 512 * 15) + 1024)/ s.sampleRate))]}.play - -// as the algorithm resynthesize the sinusoidal peaks, we would expect to get it to work almost perfectly on a sine wave, with these settings that tell the process to tolerate everything as a sinusoid, even short and quiet peaks -{FluidSines.ar(SinOsc.ar(mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 200,trackFreqRange: 1000,trackProb: 0)}.play; - -// we can listen to the artefact in solo, amplifying it by 30dB, to hear the 'lobes' - not bad at all! -{FluidSines.ar(SinOsc.ar(mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 200,trackFreqRange: 1000,trackProb: 0)[1].dup * Line.ar(0,30,1).dbamp}.play; - -// as this is a windowed process, the frequency of the peak is good for that full window, and therefore interesting artefacts appear when the pitch is changing. -{FluidSines.ar(SinOsc.ar(LFTri.kr(0.1).exprange(220,880),mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 300,trackFreqRange: 1000,trackProb: 0)}.play; - -// if we solo and amplify the artefacts, they are much more apparent (and interesting) -{FluidSines.ar(SinOsc.ar(LFTri.kr(0.1).exprange(220,880),mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 300,trackFreqRange: 1000,trackProb: 0)[1].dup * Line.ar(0,30,1).dbamp}.play; -:: diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp deleted file mode 100644 index bc914df..0000000 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ /dev/null @@ -1,325 +0,0 @@ -TITLE:: FluidSpectralShape -SUMMARY:: Seven Spectral Shape Descriptors in Real-Time -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile - -DESCRIPTION:: -This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The descriptors are: -LIST:: -##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: - LIST:: - ## the spectral centroid (1) in Hz. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in Hz. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below that frequency, i.e. the central bin of the magnitude spectrum, it is positive. - ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. - :: - ## the rolloff (5) in Hz. This indicates the frequency under which code::rolloffPercent:: of the energy is included. The default is 95%. - ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. - ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. - - The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. -:: - - The process will return a multichannel control steam with the seven values, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. - -CLASSMETHODS:: - -METHOD:: kr - The audio rate in, control rate out version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: minFreq - The minimum frequency that the algorithm will consider for computing the spectral shape. Frequencies below will be ignored. The default of 0 goes down to DC when possible. - -ARGUMENT:: maxFreq - The maximum frequency that the algorithm will consider for computing the spectral shape. Frequencies above will be ignored. The default of -1 goes up to Nyquist. - -ARGUMENT:: rolloffPercent - This sets the percentage of the frame's energy that will be reported as the rolloff frequency. The default is 95%. - -ARGUMENT:: unit - The frequency unit for the spectral shapes to be computed upon, and outputted at. The default (0) is in Hertz and computes the moments on a linear spectrum. The alternative is in MIDI note numbers(1), which compute the moments on an exponential spectrum. - -ARGUMENT:: power - This flag sets the scaling of the magnitudes in the moment calculation. It uses either its amplitude (0, by default) or its power (1). - -ARGUMENT:: windowSize - The window size. As spectral shape estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty - -ARGUMENT:: hopSize - The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). - -ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. - -ARGUMENT:: maxFFTSize - How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. - -RETURNS:: - A 7-channel KR signal with the seven spectral shape descriptors. The latency is windowSize. - - -EXAMPLES:: - - -code:: -//create a monitoring bus for the descriptors -b = Bus.new(\control,0,7); - -//create a monitoring window for the values - -( -w = Window("spectral Shape Monitor", Rect(10, 10, 220, 190)).front; - -c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("Centroid: "); -c[1].string = ("Spread: "); -c[2].string = ("Skewness: "); -c[3].string = ("Kurtosis: "); -c[4].string = ("Rolloff: "); -c[5].string = ("Flatness: "); -c[6].string = ("Crest: "); - -a = Array.fill(7, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); -) - -//run the window updating routine. -( -r = Routine { - { - - b.get({ arg val; - { - if(w.isClosed.not) { - val.do({arg item,index; - a[index].string = item.round(0.01)}) - } - }.defer - }); - - 0.01.wait; - }.loop - -}.play -) - -//play a simple sound to observe the values -( - { - var source; - source = BPF.ar(WhiteNoise.ar(), 330, 55/330); - Out.kr(b,FluidSpectralShape.kr(source)); - source.dup; - }.play; -) -:: - -STRONG::A commented tutorial on how each descriptor behaves with test signals: :: - -CODE:: -// as above, create a monitoring bus for the descriptors -b = Bus.new(\control,0,7); - -//again, create a monitoring window for the values -( -w = Window("Spectral Shape Monitor", Rect(10, 10, 220, 190)).front; - -c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("Centroid: "); -c[1].string = ("Spread: "); -c[2].string = ("Skewness: "); -c[3].string = ("Kurtosis: "); -c[4].string = ("Rolloff: "); -c[5].string = ("Flatness: "); -c[6].string = ("Crest: "); - -a = Array.fill(7, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); -) - -// this time, update a little more slowly. -( -r = Routine { - { - - b.get({ arg val; - { - if(w.isClosed.not) { - val.do({arg item,index; - a[index].string = item.round(0.01)}) - } - }.defer - }); - - 0.2.wait; - }.loop - -}.play -) - -// first, a sine wave -( -x = { - arg freq=220; - var source; - source = SinOsc.ar(freq,mul:0.1); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); - source.dup; -}.play; -) - -// at 220, the centroid is on the frequency, the spread is narrow, but as wide as the FFT Hann window ripples, the skewness is high as we are low and therefore far left of the middle bin (aka half-Nyquist), the Kurtosis is incredibly high as we have a very peaky spectrum. The rolloff is slightly higher than the frequency, taking into account the FFT windowing ripples, the flatness is incredibly low, as we have one peak and not much else, and the crest is quite high, because most of the energy is in a few peaky bins. - -x.set(\freq, 440) - -// at 440, the skewness has changed (we are nearer the middle of the spectrogram) and the Kurtosis too, although it is still so high it is quite in the same order of magnitude. The rest is stable, as expected. - -x.set(\freq, 11000) - -// at 11kHz, kurtosis is still in the thousand, but skewness is almost null, as expected. - -x.free - -// second, broadband noise -( -x = { - arg type = 0; - var source; - source = Select.ar(type,[WhiteNoise.ar(0.1),PinkNoise.ar(0.1)]); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); - source.dup; -}.play; -) - -// white noise has a linear repartition of energy, so we would expect a centroid in the middle bin (aka half-Nyquist) with a spread covering the full range (+/- a quarter-Nyquist), with a skewness almost null since we are centered, and a very low Kurtosis since we are flat. The rolloff should be almost at Nyquist, the flatness as high as it gets, and the crest quite low. - -x.set(\type, 1) - -// pink noise has a drop of 3dB per octave across the spectrum, so we would, by comparison, expect a lower centroid, a slighly higher skewness and kurtosis, a lower rolloff, a slighly lower flatness and a higher crest for the larger low-end energy. - -x.free - -// third, bands of noise -( -x = { - arg type = 0; - var source, chain; - chain = FFT(LocalBuf(1024), WhiteNoise.ar(0.5)); - chain = chain.pvcollect(1024, {arg mag,phase;[mag,phase]},5,11,1); - source = Select.ar(type,[ - BPF.ar(BPF.ar(WhiteNoise.ar(0.5),330,0.666),330,0.666), - IFFT(chain)]); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); - source.dup; -}.play; -) - -// a second-order bandpass filter on whitenoise, centred on 330Hz with one octave bandwidth, gives us a centroid quite high. This is due to the exponential behaviour of the filter, with a gentle slope. Observe the spectral analyser: - -s.freqscope - -// at first it seems quite centred, but then flip the argument FrqScl to lin(ear) and observe how high the spectrum goes. If we set it to a brickwall spectral filter tuned on the same frequencies: - -x.set(\type, 1) - -// we have a much narrower register, and our centroid and spread, as well as the kurtosis and flatness, agrees with this reading. - -x.free - -//fourth, equally spaced sines -( -x = { - arg freq = 220; - var source; - source = Mix.fill(7, {arg ind; SinOsc.ar(freq + (ind * (220 / 6)), 0, 0.02)}); - Out.kr(b,FluidSpectralShape.kr(source)); - source.dup; -}.play; -) - -// this example shows a similar result to the brickwall spectral bandpass above. If we move the central frequency nearer the half-Nyquist: - -x.set(\freq, 8800) - -// we can observe that the linear spread is kept the same, since there is the same linear distance in Hz between our frequencies. Skewness is a good indication here of where we are in the spectrum with the shape. -:: - - -STRONG::A few notes on the impact of the scale options:: - -CODE:: -// The computation of the centroids and other moments can also be done considering a logarithmic pitch scale, and/or the power of the magnitudes. This yields values that are more in line with the expectation of the users of equalisers for instance, where the shape is often drawn and described in logairhmic terms, i.e. dB per octave. - -// For instance, compare the values of the centroid and the spread in both scales: -( -{ - var source = BPF.ar(PinkNoise.ar(0.1),MouseX.kr().exprange(300,3000).poll(1,label: "filter frequency"), 0.5); - FluidSpectralShape.kr(source, minFreq: 20, maxFreq: 20000, unit: 0, power: 0)[0].lag.poll(1,"linear centroid"); - FluidSpectralShape.kr(source, minFreq: 20, maxFreq: 20000, unit: 1, power: 1)[0].lag.midicps.poll(1,"exponential centroid");//convert from midi to Hz - source.dup -}.play -) - -// The lower one gets in frequency, the more the linear spectral bias shows. The same applies to the spread: - -( -{ - var source = BPF.ar(PinkNoise.ar(0.1),440, MouseX.kr().exprange(0.1,4).poll(1,label: "filter RQ")); - FluidSpectralShape.kr(source, minFreq: 20, maxFreq: 20000, unit: 0, power: 0)[1].lag.poll(1,"linear spread"); - FluidSpectralShape.kr(source, minFreq: 20, maxFreq: 20000, unit: 1, power: 1)[1].lag.poll(1,"exponential spread"); - source.dup -}.play -) - -// The logarythmic unit is in semitones. To convert, either divide by 12 to get the octave of one standard deviation, or divide by 6 to get the width of the filter in octaves. One clear observation is that the width is now in a range that scales with what we hear, growing fourfold as the filter goes from resonating to more broadband. - -// An example of productive mapping between filters parameters and logarithmic centroid values allows to make a simple automatic subtractive noise resynthesis - -// load a beat -b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav")); - -//logcentroid version -( -{ - var source = PlayBuf.ar(1,b,loop: 1); - var loudness, centroid, spread; - - #centroid,spread = Lag.kr(FluidSpectralShape.kr(source, minFreq: 20, maxFreq: 20000, unit: 1, power: 1, hopSize: 128),128/SampleRate.ir); - loudness = Lag.kr(FluidLoudness.kr(source,hopSize: 128),128/SampleRate.ir); - [ - DelayN.ar(source,delaytime: 1024/SampleRate.ir), - BBandPass.ar(WhiteNoise.ar(), - centroid.midicps, - (spread/6), - loudness[0].dbamp * 2 - ) - ] -}.scope; -) - -//lincentroid version for comparison -( -{ - var source = PlayBuf.ar(1,b,loop: 1); - var loudness, centroid, spread; - - #centroid,spread = Lag.kr(FluidSpectralShape.kr(source, minFreq: 20, maxFreq: 20000, unit: 0, power: 0, hopSize: 128),128/SampleRate.ir); - loudness = Lag.kr(FluidLoudness.kr(source,hopSize: 128),128/SampleRate.ir); - [ - DelayN.ar(source,delaytime: 1024/SampleRate.ir), - Sanitize.ar(BBandPass.ar(WhiteNoise.ar(), - centroid, - (spread * 2/centroid).max(0.001), - loudness[0].dbamp * 2 - )) - ] -}.scope; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp deleted file mode 100644 index f2272e1..0000000 --- a/release-packaging/HelpSource/Classes/FluidStandardize.schelp +++ /dev/null @@ -1,150 +0,0 @@ -TITLE:: FluidStandardize -summary:: Standardize a FluidDataSet -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidDataSet, Classes/FluidNormalize, Classes/FluidRobustScale - -DESCRIPTION:: -Standardize a link::Classes/FluidDataSet::, i.e. rescale using its mean(s) and standard deviation(s) in each dimension. - -See http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html - -CLASSMETHODS:: - -METHOD:: new -Create a new instance -ARGUMENT:: server -The server for this model -ARGUMENT:: invert -The direction in which the standardization will occur for transform and transformpoint. The default 0 is taking in the range of the input used to fit and transforms it towards the standardized range. A value of 1 will expect an input of the standardized range to transform back to the original range. - -INSTANCEMETHODS:: - -METHOD:: fit -Fit model to a DataSet without applying scaling -ARGUMENT:: dataSet -The link::Classes/FluidDataSet:: to standardize -ARGUMENT:: action -A function to run when processing is complete - -METHOD:: transform -Standardize a link::Classes/FluidDataSet::, using the learned statistics from a previous call to link::Classes/FluidStandardize#fit:: -ARGUMENT:: sourceDataSet -The link::Classes/FluidDataSet:: to standardize -ARGUMENT:: destDataSet -The link::Classes/FluidDataSet:: to populate with standardized data -ARGUMENT:: action -A function to run when processing is complete - -METHOD:: fitTransform -Standardize a link::Classes/FluidDataSet:: into another link::Classes/FluidDataSet:: -ARGUMENT:: sourceDataSet -The link::Classes/FluidDataSet:: to standardize -ARGUMENT:: destDataSet -The link::Classes/FluidDataSet:: to populate with standardized data -ARGUMENT:: action -A function to run when processing is complete - -METHOD:: transformPoint -Standardize a new data point, using the learned statistics from a previous call to link::Classes/FluidStandardize#fit:: -ARGUMENT:: sourceBuffer -A link::Classes/Buffer:: with the new data point -ARGUMENT:: destBuffer -A link::Classes/Buffer:: to contain the standardize value -ARGUMENT:: action -A function to run when processing is complete - -EXAMPLES:: -code:: -s.boot; -//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidStandardize -( -~audiofile = FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav"); -~raw = FluidDataSet(s); -~stand = FluidDataSet(s); -~audio = Buffer.read(s,~audiofile); -~pitch_feature = Buffer.new(s); -~stats = Buffer.alloc(s, 7, 2); -~standardizer = FluidStandardize(s); -) - - -// Load audio and run a pitch analysis, which gives us pitch and pitch confidence (so a 2D datum) -( -~audio = Buffer.read(s,~audiofile); -FluidBufPitch.process(s,~audio, features: ~pitch_feature,action:{"Analysed Pitch".postln}); -) - -// Divide the time series in to 10, and take the mean of each s"egment and add this as a point to -// the 'raw' FluidDataSet -( -{ - var trig = LocalIn.kr(1, 1); - var buf = LocalBuf(2, 1); - var count = PulseCount.kr(trig) - 1; - var chunkLen = (~pitch_feature.numFrames / 10).asInteger; - var stats = FluidBufStats.kr( - source: ~pitch_feature, startFrame: count * chunkLen, - numFrames: chunkLen, stats: ~stats, - trig: trig * (count < 10), blocking: 1 - ); - 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, idNumber: count, trig: Done.kr(stats)); - LocalOut.kr( Done.kr(dsWr)); - FreeSelf.kr(count - 9); - Poll.kr(trig,count, \count); -}.play; -) - -// Standardize and load to language-side array -( -~rawarray = Array.new(10); -~stdarray= Array.new(10); -~standardizer.fitTransform(~raw,~stand, { - ~raw.dump{|x| 10.do{|i| - ~rawarray.add(x["data"][i.asString]) - }}; - ~stand.dump{|x| 10.do{|i| - ~stdarray.add(x["data"][i.asString]) - }}; -}); -) - -( -(~rawarray ++ 0).flop.plot("Unstandardized",Rect(0,0,400,400),minval:0,maxval:[5000,1]).plotMode=\bars; -(~stdarray ++ 0).flop.plot("Standardized",Rect(410,0,400,400), minval:-2,maxval:2).plotMode=\bars; -) - -// single point transform on arbitrary value -~inbuf = Buffer.loadCollection(s,0.5.dup); -~outbuf = Buffer.new(s); -~standardizer.transformPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,2,{|y|y.postln;};)}); - -:: - -subsection::Server Side Querying -code:: -( -// read frames out of buffer and pass to standardize -{ - var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); - var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:99); - var trig = A2K.kr(HPZ1.ar(counter) < 0); - //average 10 frames: one could use the MovingAverage extension here - var avg; - var inputPoint= LocalBuf(2); - var outputPoint = LocalBuf(2); - var avgBuf = LocalBuf(100,2); - //average of pitch features - BufWr.kr(FluidPitch.kr(audio),avgBuf,phase:counter); - avg = Mix.new(BufRd.kr(2, avgBuf, phase:100.collect{|x|x})) * 0.01; - //assemble data point - BufWr.kr(avg[0],inputPoint,0); - BufWr.kr(avg[1],inputPoint,1); - Poll.kr(trig,BufRd.kr(1,inputPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); - ~standardizer.kr(trig,inputPoint,outputPoint); - Poll.kr(trig,BufRd.kr(1,outputPoint,[0,1]),["pitch (standardized)", "confidence (standardized)"]); -}.play; -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidStats.schelp b/release-packaging/HelpSource/Classes/FluidStats.schelp deleted file mode 100644 index 1cfc2d3..0000000 --- a/release-packaging/HelpSource/Classes/FluidStats.schelp +++ /dev/null @@ -1,47 +0,0 @@ -TITLE:: FluidStats -summary:: Rolling mean and standard devication on kr inputs -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidBufStats, Classes/FluidStandardize - -DESCRIPTION:: -Computes the rolling mean and sample standard deviation over a given window for multichannel kr inputs. - -This UGen does not perform multichannel expansion because (like link::Classes/BufWr::) it takes an array as input. - -CLASSMETHODS:: - -METHOD:: kr -Run the UGen - -ARGUMENT:: inputsArray -An array (or just one) kr input stream. - -ARGUMENT:: size -The size of the history window to use - -returns:: A 2D array of kr outputs code::[[means][standard deviations]]:: - -INSTANCEMETHODS:: - -PRIVATE:: numOutputs, initOutputs, init, channels,checkInputs - -EXAMPLES:: - -code:: -( -~gaussianNoise = { |a,b| - var mag = (-2 * a.abs.log).sqrt; - var f = 2 * pi * b.abs; - [mag * f.cos, mag * f.sin] -} - - - -{ - var src = ~gaussianNoise.value(WhiteNoise.kr,WhiteNoise.kr); - var stats = FluidStats.kr(src,20); - stats[0].poll(label:'means'); - stats[1].poll(label:'standard deviations'); -}.play -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp deleted file mode 100644 index e7e7c09..0000000 --- a/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp +++ /dev/null @@ -1,73 +0,0 @@ -TITLE:: FluidTransientSlice -SUMMARY:: Transient-Based Real-Time Audio Slicer -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -DESCRIPTION:: -This class implements a real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - -The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: order - The order in samples of the impulse response filter used to model the estimated continuous signal. It is how many previous samples are used by the algorithm to predict the next one as reference for the model. The higher the order, the more accurate is its spectral definition, not unlike fft, improving low frequency resolution, but it differs in that it is not connected to its temporal resolution. - -ARGUMENT:: blockSize - The size in samples of frame on which it the algorithm is operating. High values are more cpu intensive, and also determines the maximum transient size, which will not be allowed to be more than half that lenght in size. - -ARGUMENT:: padSize - The size of the handles on each sides of the block simply used for analysis purpose and avoid boundary issues. - -ARGUMENT:: skew - The nervousness of the bespoke detection function with values from -10 to 10. It allows to decide how peaks are amplified or smoothed before the thresholding. High values increase the sensitivity to small variations. - -ARGUMENT:: threshFwd - The threshold of the onset of the smoothed error function. It allows tight start of the identification of the anomaly as it proceeds forward. - -ARGUMENT:: threshBack - The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. - -ARGUMENT:: windowSize - The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positives. - -ARGUMENT:: clumpLength - The window size in sample within with positive detections will be clumped together to avoid overdetecting in time. - -ARGUMENT:: minSliceLength - The minimum duration of a slice in samples. - -RETURNS:: - An audio stream with impulses at detected transients. The latency between the input and the output is (blockSize + padSize - order) samples. - - -EXAMPLES:: - -CODE:: -//load some sounds -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); - -// basic param (the process add a latency of (blockSize + padSize - order) samples -{var sig = PlayBuf.ar(1,b,loop:1); [FluidTransientSlice.ar(sig) * 0.5, DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate))]}.play - -// other parameters -{var sig = PlayBuf.ar(1,b,loop:1); [FluidTransientSlice.ar(sig,order:80,minSliceLength:2205) * 0.5, DelayN.ar(sig, 1, ((256 + 128 - 80)/ s.sampleRate))]}.play - -// More musical, transient-trigged autopan -( -{ - var sig, trig, syncd, pan; - sig = PlayBuf.ar(1,b,loop:1); - trig = FluidTransientSlice.ar(sig,order:10,minSliceLength:4410); - syncd = DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate)); - pan = TRand.ar(-1,1,trig); - Pan2.ar(syncd,pan); -}.play -) -:: diff --git a/release-packaging/HelpSource/Classes/FluidTransients.schelp b/release-packaging/HelpSource/Classes/FluidTransients.schelp deleted file mode 100644 index 7c93aaa..0000000 --- a/release-packaging/HelpSource/Classes/FluidTransients.schelp +++ /dev/null @@ -1,65 +0,0 @@ -TITLE:: FluidTransients -SUMMARY:: Real-Time Transient Modeller and Extractor -CATEGORIES:: Libraries>FluidCorpusManipulation -RELATED:: Guides/FluidCorpusManipulation - -DESCRIPTION:: -This class applies a real-time transient extractor on its input. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/ - - The algorithm will take an audio in, and will divide it in two outputs: LIST:: - ## the transients, estimated from the signal and extracted from it; - ## the remainder of the material, as estimated by the algorithm. :: - - The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. - -CLASSMETHODS:: - -METHOD:: ar - The audio rate version of the object. - -ARGUMENT:: in - The audio to be processed. - -ARGUMENT:: order - The order in samples of the impulse response filter used to model the estimated continuous signal. It is how many previous samples are used by the algorithm to predict the next one as reference for the model. The higher the order, the more accurate is its spectral definition, not unlike fft, improving low frequency resolution, but it differs in that it is not conected to its temporal resolution. - -ARGUMENT:: blockSize - The size in samples of frame on which it the algorithm is operating. High values are more cpu intensive, and also determines the maximum transient size, which will not be allowed to be more than half that lenght in size. - -ARGUMENT:: padSize - The size of the handles on each sides of the block simply used for analysis purpose and avoid boundary issues. - -ARGUMENT:: skew - The nervousness of the bespoke detection function with values from -10 to 10. It allows to decide how peaks are amplified or smoothed before the thresholding. High values increase the sensitivity to small variations. - -ARGUMENT:: threshFwd - The threshold of the onset of the smoothed error function. It allows tight start of the identification of the anomaly as it proceeds forward. - -ARGUMENT:: threshBack - The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. - -ARGUMENT:: windowSize - The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positive. - -ARGUMENT:: clumpLength - The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. - -RETURNS:: - An array of two audio streams: [0] is the transient extracted, [1] is the rest. The latency between the input and the output is (blockSize + padSize - order) samples. - - -EXAMPLES:: - -CODE:: -//load some buffer -b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav")); - -// basic parameters -{FluidTransients.ar(PlayBuf.ar(1, b, loop:1))}.play - -// tweaked parameters -{FluidTransients.ar(PlayBuf.ar(1, b, loop:1), 80, threshFwd:MouseX.kr(0,5), threshBack:MouseY.kr(0,2))}.play - -// null test (the process add a latency of (blockSize + padding - order) samples -{var sig = PlayBuf.ar(1, b, loop:1); [FluidTransients.ar(sig).sum - DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate))]}.play -:: diff --git a/release-packaging/HelpSource/Classes/FluidUMAP.schelp b/release-packaging/HelpSource/Classes/FluidUMAP.schelp deleted file mode 100644 index 38f794a..0000000 --- a/release-packaging/HelpSource/Classes/FluidUMAP.schelp +++ /dev/null @@ -1,247 +0,0 @@ -TITLE:: FluidUMAP -summary:: Dimensionality Reduction with Uniform Manifold Approximation and Projection -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidMDS, Classes/FluidPCA, Classes/FluidDataSet - -DESCRIPTION:: - -Multidimensional scaling of a link::Classes/FluidDataSet:: using Uniform Manifold Approximation and Projection (UMAP) - -Please refer to https://umap-learn.readthedocs.io/ for more information on the algorithm. - -CLASSMETHODS:: - -METHOD:: new -Make a new instance -ARGUMENT:: server -The server on which to run this model -ARGUMENT:: numDimensions -The number of dimensions to reduce to -ARGUMENT:: numNeighbours -The number of neighbours considered by the algorithm to balance local vs global structures to conserve. Low values will prioritise preserving local structure, high values will help preserving the global structure. -ARGUMENT:: minDist -The minimum distance each point is allowed to be from the others in the low dimension space. Low values will make tighter clumps, and higher will spread the points more. -ARGUMENT:: iterations -The number of iterations that the algorithm will go through to optimise the new representation -ARGUMENT:: learnRate -The learning rate of the algorithm, aka how much of the error it uses to estimate the next iteration. - -INSTANCEMETHODS:: - -PRIVATE:: init - -METHOD:: fit -Train this model on a link::Classes/FluidDataSet:: but don't transform the data -ARGUMENT:: dataSet -A link::Classes/FluidDataSet:: to analyse -ARGUMENT:: action -Run when done - -METHOD:: transform -Given a trained model, apply the reduction to a source link::Classes/FluidDataSet:: and write to a destination. Can be the same for both (in-place) -ARGUMENT:: sourceDataSet -Source data, or the DataSet name -ARGUMENT:: destDataSet -Destination data, or the DataSet name -ARGUMENT:: action -Run when done. - -METHOD:: fitTransform -Fit the model to a link::Classes/FluidDataSet:: and write the new projected data to a destination FluidDataSet. -ARGUMENT:: sourceDataSet -Source data, or the DataSet name -ARGUMENT:: destDataSet -Destination data, or the DataSet name -ARGUMENT:: action -Run when done - -EXAMPLES:: - -code:: - -//Preliminaries: we want some points, a couple of FluidDataSets, a FluidStandardize and a FluidUMAP -( -~raw = FluidDataSet(s); -~standardized = FluidDataSet(s); -~reduced = FluidDataSet(s); -~normalized = FluidDataSet(s); -~standardizer = FluidStandardize(s); -~normalizer = FluidNormalize(s, 0.05, 0.95); -~umap = FluidUMAP(s).numDimensions_(2).numNeighbours_(5).minDist_(0.2).iterations_(50).learnRate_(0.2); -) - - -// build a dataset of 400 points in 3D (colour in RGB) -~colours = Dictionary.newFrom(400.collect{|i|[("entry"++i).asSymbol, 3.collect{1.0.rand}]}.flatten(1)); -~raw.load(Dictionary.newFrom([\cols, 3, \data, ~colours])); - -// check the entries -~raw.print; - -//First standardize our DataSet, then apply the UMAP to get 2 dimensions, then normalise these 2 for drawing in the full window size -( -~standardizer.fitTransform(~raw,~standardized,action:{"Standardized".postln}); -~umap.fitTransform(~standardized,~reduced,action:{"Finished UMAP".postln}); -~normalizer.fitTransform(~reduced,~normalized,action:{"Normalized Output".postln}); -) -//we recover the reduced dataset -~normalized.dump{|x| ~normalizedDict = x["data"]}; - -~normalized.print -~normalizedDict.postln - -//Visualise the 2D projection of our original 4D data -( -w = Window("a perspective", Rect(128, 64, 200, 200)); -w.drawFunc = { - Pen.use { - ~normalizedDict.keysValuesDo{|key, val| - Pen.fillColor = Color.new(~colours[key.asSymbol][0], ~colours[key.asSymbol][1],~colours[key.asSymbol][2]); - Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); - ~colours[key.asSymbol].flat; - } - } -}; -w.refresh; -w.front; -) - -//play with parameters -~umap.numNeighbours_(10).minDist_(0.5).iterations_(100).learnRate_(0.1); - -//rerun the UMAP -~umap.fitTransform(~standardized,~reduced,action:{"Finished UMAP".postln}); - -//draw to compare -( -~normalizer.fitTransform(~reduced,~normalized,action:{ - "Normalized Output".postln; - ~normalized.dump{|x| - ~normalizedDict = x["data"]; - - { - u = Window("another perspective", Rect(328, 64, 200, 200)); - u.drawFunc = { - Pen.use { - ~normalizedDict.keysValuesDo{|key, val| - Pen.fillColor = Color.new(~colours[key.asSymbol][0], ~colours[key.asSymbol][1],~colours[key.asSymbol][2]); - Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); - ~colours[key.asSymbol].flat; - }; - }; - }; - u.refresh; - u.front; - - }.defer; - - }; -}); -) - -// now run new random points on the same training material. Colours should be scattered around the same space - -~newDS = FluidDataSet(s); -~colours2 = Dictionary.newFrom(400.collect{|i|[("entry"++i).asSymbol, 3.collect{1.0.rand}]}.flatten(1)); -~newDS.load(Dictionary.newFrom([\cols, 3, \data, ~colours2])); - -//we need to standardize to the same space -~newDSstan = FluidDataSet(s); -~standardizer.transform(~newDS, ~newDSstan); - -//then we can run the umap -~newDSmap = FluidDataSet(s); -~umap.transform(~newDSstan, ~newDSmap); - -//then we can draw and look -( -~normalizer.transform(~newDSmap,~normalized,action:{ - "Normalized Output".postln; - ~normalized.dump{|x| - ~normalizedDict = x["data"]; - { - t = Window("new material", Rect(528, 64, 200, 200)); - t.drawFunc = { - Pen.use { - ~normalizedDict.keysValuesDo{|key, val| - Pen.fillColor = Color.new(~colours2[key.asSymbol][0], ~colours2[key.asSymbol][1],~colours2[key.asSymbol][2]); - Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); - ~colours2[key.asSymbol].flat; - }; - }; - }; - t.refresh; - t.front; - }.defer; - }; -}); -) - -//if we process the original dataset, we will see small differences in positions -~reduced2 = FluidDataSet(s); -~umap.transform(~standardized, ~reduced2, action: {\done.postln;}); - -//then we can draw and look -( -~normalizer.transform(~reduced2,~normalized,action:{ - "Normalized Output".postln; - ~normalized.dump{|x| - ~normalizedDict = x["data"]; - { - z = Window("old material", Rect(728, 64, 200, 200)); - z.drawFunc = { - Pen.use { - ~normalizedDict.keysValuesDo{|key, val| - Pen.fillColor = Color.new(~colours[key.asSymbol][0], ~colours[key.asSymbol][1],~colours[key.asSymbol][2]); - Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); - ~colours[key.asSymbol].flat; - }; - }; - }; - z.refresh; - z.front; - }.defer; - }; -}); -) - -//this is because the fitTransform method has the advantage of being certain that the data it transforms is the one that has been used to fit the model. This allows for more accurate distance measurement. - -//to check, let's retrieve a single point and predict its position -( -~sourcePoint = Buffer(s); -~original = Buffer(s); -~standed = Buffer(s); -~umaped = Buffer(s); -) - -//retrieve the 3D original -~raw.getPoint("entry49",~sourcePoint) -//retrieve the fitTransformed point as the most accurate point -~reduced.getPoint("entry49",~original, {~original.getn(0,2,{|x|x.postln})}) -//retreive the transformed point, via the standardizer -~standardizer.transformPoint(~sourcePoint,~standed); -~umap.transformPoint(~standed, ~umaped, {~umaped.getn(0,2,{|x|x.postln})}) - -// one can also retrieve in control rate with Server Side Queries -// Let's map our learned UMAP dimensions to the controls of a processor - -( -{ - var trig = Impulse.kr(1); - var point = WhiteNoise.kr(1.dup(3)); - var inputPoint = LocalBuf(3); - var standPoint = LocalBuf(3); - var outputPoint = LocalBuf(2); - var cue1, cue2; - Poll.kr(trig, point, [\pointX,\pointY,\pointZ]); - point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; - cue1 = ~standardizer.kr(trig,inputPoint,standPoint); - Poll.kr(cue1,BufRd.kr(1,standPoint,(0..2),interpolation:0),[\stdX,\stdY, \stdZ]); - cue2 = ~umap.kr(cue1, standPoint, outputPoint); - Poll.kr(cue2,BufRd.kr(1,outputPoint,[0,1],interpolation:0),[\newDimA,\newDimB]); - Silent.ar; -}.play; -) - -:: From 267817366193e4f54ce3bf51025f373f5c06b140 Mon Sep 17 00:00:00 2001 From: Ted Moore Date: Mon, 8 Aug 2022 16:18:09 +0100 Subject: [PATCH 6/7] fix for issue #120 --- release-packaging/Classes/FluidBufLoudness.sc | 12 ++++++------ release-packaging/Classes/FluidBufSTFT.sc | 7 ------- release-packaging/Classes/FluidBufTransientSlice.sc | 12 ++++++------ 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/release-packaging/Classes/FluidBufLoudness.sc b/release-packaging/Classes/FluidBufLoudness.sc index 6b95a20..611c228 100644 --- a/release-packaging/Classes/FluidBufLoudness.sc +++ b/release-packaging/Classes/FluidBufLoudness.sc @@ -29,8 +29,8 @@ FluidBufLoudness : FluidBufProcessor{ source = source.asUGenInput; features = features.asUGenInput; - source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; - features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking); } @@ -44,8 +44,8 @@ FluidBufLoudness : FluidBufProcessor{ source = source.asUGenInput; features = features.asUGenInput; - source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; - features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^this.new( server, nil, [features] @@ -63,8 +63,8 @@ FluidBufLoudness : FluidBufProcessor{ source = source.asUGenInput; features = features.asUGenInput; - source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; - features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^this.new( server, nil, [features] diff --git a/release-packaging/Classes/FluidBufSTFT.sc b/release-packaging/Classes/FluidBufSTFT.sc index 67245a0..0c2aa7b 100644 --- a/release-packaging/Classes/FluidBufSTFT.sc +++ b/release-packaging/Classes/FluidBufSTFT.sc @@ -2,9 +2,6 @@ FluidBufSTFT : FluidBufProcessor { *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0,windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 1| - // source = source.asUGenInput; - - // source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; source = source ? -1; magnitude = magnitude ? -1; phase = phase ? -1; @@ -15,9 +12,6 @@ FluidBufSTFT : FluidBufProcessor { *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - // source = source.asUGenInput; - - // source.isNil.if {"FluidBufSTFT: Invalid source buffer".throw}; source = source ? -1; magnitude = magnitude ? -1; phase = phase ? -1; @@ -32,7 +26,6 @@ FluidBufSTFT : FluidBufProcessor { *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1,freeWhenDone = true, action| - // source = source.asUGenInput; source = source ? -1; magnitude = magnitude ? -1; phase = phase ? -1; diff --git a/release-packaging/Classes/FluidBufTransientSlice.sc b/release-packaging/Classes/FluidBufTransientSlice.sc index 81bff0c..201637e 100644 --- a/release-packaging/Classes/FluidBufTransientSlice.sc +++ b/release-packaging/Classes/FluidBufTransientSlice.sc @@ -5,8 +5,8 @@ FluidBufTransientSlice : FluidBufProcessor { source = source.asUGenInput; indices = indices.asUGenInput; - source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + indices.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking); } @@ -16,8 +16,8 @@ FluidBufTransientSlice : FluidBufProcessor { source = source.asUGenInput; indices = indices.asUGenInput; - source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + indices.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^this.new( server, nil,[indices] @@ -30,8 +30,8 @@ FluidBufTransientSlice : FluidBufProcessor { source = source.asUGenInput; indices = indices.asUGenInput; - source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + indices.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^this.new( server, nil,[indices] From 17067137fdd3f399c18675f288e2ec90ba1512e0 Mon Sep 17 00:00:00 2001 From: gianlucaelia Date: Fri, 12 Aug 2022 12:21:58 +0100 Subject: [PATCH 7/7] Classes: uniform sc default indentation (#134) SuperCollider code defaults to indentation using tab. All sc codebase is formatted like this. Here I just opened every .sc file in scide, selected all code, and automatically indent it pressing Tab. --- release-packaging/Classes/FluidAmpGate.sc | 2 +- .../Classes/FluidAudioTransport.sc | 12 +- .../Classes/FluidBufAmpFeature.sc | 40 +- release-packaging/Classes/FluidBufAmpGate.sc | 42 +- release-packaging/Classes/FluidBufAmpSlice.sc | 40 +- .../Classes/FluidBufAudioTransport.sc | 44 +- release-packaging/Classes/FluidBufChroma.sc | 40 +- release-packaging/Classes/FluidBufCompose.sc | 48 +- release-packaging/Classes/FluidBufFlatten.sc | 46 +- release-packaging/Classes/FluidBufHPSS.sc | 50 +- release-packaging/Classes/FluidBufLoudness.sc | 114 ++-- release-packaging/Classes/FluidBufMFCC.sc | 48 +- release-packaging/Classes/FluidBufMelBands.sc | 40 +- release-packaging/Classes/FluidBufNMF.sc | 50 +- release-packaging/Classes/FluidBufNMFCross.sc | 46 +- release-packaging/Classes/FluidBufNMFSeed.sc | 38 +- .../Classes/FluidBufNoveltyFeature.sc | 28 +- .../Classes/FluidBufNoveltySlice.sc | 46 +- .../Classes/FluidBufOnsetFeature.sc | 64 +- .../Classes/FluidBufOnsetSlice.sc | 64 +- release-packaging/Classes/FluidBufPitch.sc | 84 +-- release-packaging/Classes/FluidBufSTFT.sc | 38 +- release-packaging/Classes/FluidBufScale.sc | 38 +- release-packaging/Classes/FluidBufSelect.sc | 64 +- .../Classes/FluidBufSelectEvery.sc | 38 +- release-packaging/Classes/FluidBufSines.sc | 36 +- .../Classes/FluidBufSpectralShape.sc | 16 +- release-packaging/Classes/FluidBufStats.sc | 84 +-- .../Classes/FluidBufThreadDemo.sc | 20 +- release-packaging/Classes/FluidBufThresh.sc | 54 +- .../Classes/FluidBufTransientSlice.sc | 44 +- .../Classes/FluidBufTransients.sc | 44 +- release-packaging/Classes/FluidChroma.sc | 6 +- release-packaging/Classes/FluidDataSet.sc | 108 +-- .../Classes/FluidDataSetQuery.sc | 87 ++- release-packaging/Classes/FluidDataSetWr.sc | 16 +- release-packaging/Classes/FluidGrid.sc | 16 +- release-packaging/Classes/FluidHPSS.sc | 16 +- release-packaging/Classes/FluidKDTree.sc | 74 +-- release-packaging/Classes/FluidKMeans.sc | 34 +- .../Classes/FluidKNNClassifier.sc | 58 +- .../Classes/FluidKNNRegressor.sc | 56 +- release-packaging/Classes/FluidLabelSet.sc | 54 +- release-packaging/Classes/FluidLoudness.sc | 48 +- release-packaging/Classes/FluidMDS.sc | 20 +- release-packaging/Classes/FluidMFCC.sc | 6 +- release-packaging/Classes/FluidMLP.sc | 262 ++++---- .../Classes/FluidManipulationJSON.sc | 16 +- release-packaging/Classes/FluidMelBands.sc | 6 +- .../Classes/FluidMessageResponse.sc | 64 +- release-packaging/Classes/FluidNMFFilter.sc | 8 +- release-packaging/Classes/FluidNMFMatch.sc | 8 +- release-packaging/Classes/FluidNRTProcess.sc | 36 +- release-packaging/Classes/FluidNormalize.sc | 104 +-- .../Classes/FluidNoveltyFeature.sc | 18 +- .../Classes/FluidNoveltySlice.sc | 18 +- .../Classes/FluidOnsetFeature.sc | 4 +- release-packaging/Classes/FluidOnsetSlice.sc | 4 +- release-packaging/Classes/FluidPCA.sc | 112 ++-- release-packaging/Classes/FluidPitch.sc | 46 +- release-packaging/Classes/FluidRobustScale.sc | 94 +-- release-packaging/Classes/FluidSKMeans.sc | 34 +- release-packaging/Classes/FluidSTFTPass.sc | 2 +- release-packaging/Classes/FluidServerCache.sc | 54 +- .../Classes/FluidServerObject.sc | 618 +++++++++--------- release-packaging/Classes/FluidSines.sc | 2 +- .../Classes/FluidSpectralShape.sc | 46 +- release-packaging/Classes/FluidStandardize.sc | 84 +-- release-packaging/Classes/FluidStats.sc | 26 +- release-packaging/Classes/FluidUMAP.sc | 106 +-- release-packaging/Classes/FluidWaveform.sc | 2 +- 71 files changed, 1917 insertions(+), 1918 deletions(-) diff --git a/release-packaging/Classes/FluidAmpGate.sc b/release-packaging/Classes/FluidAmpGate.sc index a751bb0..84a00fa 100644 --- a/release-packaging/Classes/FluidAmpGate.sc +++ b/release-packaging/Classes/FluidAmpGate.sc @@ -5,7 +5,7 @@ FluidAmpGate : FluidRTUGen { checkInputs { if(inputs.at(12).rate != 'scalar') { ^(": maxSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidAudioTransport.sc b/release-packaging/Classes/FluidAudioTransport.sc index 49a3357..0b8cf64 100644 --- a/release-packaging/Classes/FluidAudioTransport.sc +++ b/release-packaging/Classes/FluidAudioTransport.sc @@ -1,11 +1,11 @@ FluidAudioTransport : FluidRTUGen { - init { |...theInputs| - theInputs; - inputs = theInputs; - this.specialIndex = 1; //two audio inputs - // ^this.initOutputs(1,rate); - } + init { |...theInputs| + theInputs; + inputs = theInputs; + this.specialIndex = 1; //two audio inputs + // ^this.initOutputs(1,rate); + } *ar { arg in = 0, in2 = 0, interpolation = 0.0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1; ^this.multiNew('audio', in.asAudioRateInput, in2, interpolation, windowSize, hopSize, fftSize, maxFFTSize) diff --git a/release-packaging/Classes/FluidBufAmpFeature.sc b/release-packaging/Classes/FluidBufAmpFeature.sc index b0b3eaf..92be7e2 100644 --- a/release-packaging/Classes/FluidBufAmpFeature.sc +++ b/release-packaging/Classes/FluidBufAmpFeature.sc @@ -1,38 +1,38 @@ FluidBufAmpFeature : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, trig = 1, blocking = 0| - - source = source.asUGenInput; - features = features.asUGenInput; - - source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw}; - + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, trig = 1, blocking = 0| + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw}; + ^FluidProxyUgen.kr(\FluidBufAmpFeatureTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, fastRampUp, fastRampDown, slowRampUp, slowRampDown, floor, highPassFreq, trig, blocking); } - *process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action | + *process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action | - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw}; + source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw}; - ^this.new(server, nil, [features]).processList( + ^this.new(server, nil, [features]).processList( [source, startFrame, numFrames, startChan, numChans, features, fastRampUp, fastRampDown, slowRampUp, slowRampDown, floor, highPassFreq,0],freeWhenDone, action ); } - *processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action | + *processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action | - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw}; + source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw}; - ^this.new(server, nil, [features]).processList( + ^this.new(server, nil, [features]).processList( [source, startFrame, numFrames, startChan, numChans, features, fastRampUp, fastRampDown, slowRampUp, slowRampDown, floor, highPassFreq,1],freeWhenDone, action ); } diff --git a/release-packaging/Classes/FluidBufAmpGate.sc b/release-packaging/Classes/FluidBufAmpGate.sc index c644b8d..f0cb6f0 100644 --- a/release-packaging/Classes/FluidBufAmpGate.sc +++ b/release-packaging/Classes/FluidBufAmpGate.sc @@ -1,44 +1,44 @@ FluidBufAmpGate : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1, blocking = 0| - - var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1, blocking = 0| + + var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); + + source = source.asUGenInput; + indices = indices.asUGenInput; - source = source.asUGenInput; - indices = indices.asUGenInput; - ^FluidProxyUgen.kr(\FluidBufAmpGateTrigger,-1, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq,maxSize, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action | + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action | - var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); + var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); source = source ? -1; - indices = indices ? -1; + indices = indices ? -1; ^this.new( server, nil, [indices] ).processList( [source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 0],freeWhenDone,action - ); + ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action | + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action | - var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); + var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); + + source = source ? -1; + indices = indices ? -1; - source = source ? -1; - indices = indices ? -1; + ^this.new( + server, nil, [indices] + ).processList( + [source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 1],freeWhenDone,action + ); + } - ^this.new( - server, nil, [indices] - ).processList( - [source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 1],freeWhenDone,action - ); - } - } FluidBufAmpGateTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufAmpSlice.sc b/release-packaging/Classes/FluidBufAmpSlice.sc index 2787176..8fc2778 100644 --- a/release-packaging/Classes/FluidBufAmpSlice.sc +++ b/release-packaging/Classes/FluidBufAmpSlice.sc @@ -1,38 +1,38 @@ FluidBufAmpSlice : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1, blocking = 0| - - source = source.asUGenInput; - indices = indices.asUGenInput; - - source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; - + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1, blocking = 0| + + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; + ^FluidProxyUgen.kr(\FluidBufAmpSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking); } - *process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action | + *process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action | - source = source.asUGenInput; - indices = indices.asUGenInput; + source = source.asUGenInput; + indices = indices.asUGenInput; - source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; + source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; - ^this.new(server, nil, [indices]).processList( + ^this.new(server, nil, [indices]).processList( [source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,0],freeWhenDone, action ); } - *processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action | + *processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action | - source = source.asUGenInput; - indices = indices.asUGenInput; + source = source.asUGenInput; + indices = indices.asUGenInput; - source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; + source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; - ^this.new(server, nil, [indices]).processList( + ^this.new(server, nil, [indices]).processList( [source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,1],freeWhenDone, action ); } diff --git a/release-packaging/Classes/FluidBufAudioTransport.sc b/release-packaging/Classes/FluidBufAudioTransport.sc index 33a4477..a061051 100644 --- a/release-packaging/Classes/FluidBufAudioTransport.sc +++ b/release-packaging/Classes/FluidBufAudioTransport.sc @@ -2,14 +2,14 @@ FluidBufAudioTransport : FluidBufProcessor { *kr { |sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation = 0.0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; - sourceA = sourceA.asUGenInput; - sourceB = sourceB.asUGenInput; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + sourceA = sourceA.asUGenInput; + sourceB = sourceB.asUGenInput; - destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; - destination = destination.asUGenInput; + destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; + destination = destination.asUGenInput; ^FluidProxyUgen.kr(this.objectClassName++\Trigger,-1, sourceA, startFrameA, numFramesA, startChanA, numChansA, sourceB, startFrameA, numFramesA, startChanB, numChansB, destination, interpolation, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); @@ -18,14 +18,14 @@ FluidBufAudioTransport : FluidBufProcessor { *process { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; - sourceA = sourceA.asUGenInput; - sourceB = sourceB.asUGenInput; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + sourceA = sourceA.asUGenInput; + sourceB = sourceB.asUGenInput; - destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; - destination = destination.asUGenInput; + destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; + destination = destination.asUGenInput; ^this.new( server, nil, [destination] @@ -34,16 +34,16 @@ FluidBufAudioTransport : FluidBufProcessor { ) } - *processBlocking { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *processBlocking { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; - sourceA = sourceA.asUGenInput; - sourceB = sourceB.asUGenInput; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + sourceA = sourceA.asUGenInput; + sourceB = sourceB.asUGenInput; - destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; - destination = destination.asUGenInput; + destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; + destination = destination.asUGenInput; ^this.new( server, nil, [destination] diff --git a/release-packaging/Classes/FluidBufChroma.sc b/release-packaging/Classes/FluidBufChroma.sc index 14cbf16..34c9e42 100644 --- a/release-packaging/Classes/FluidBufChroma.sc +++ b/release-packaging/Classes/FluidBufChroma.sc @@ -1,49 +1,49 @@ FluidBufChroma : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufChroma: Invalid source buffer".throw}; - features.isNil.if {"FluidBufChroma: Invalid features buffer".throw}; + source.isNil.if {"FluidBufChroma: Invalid source buffer".throw}; + features.isNil.if {"FluidBufChroma: Invalid features buffer".throw}; ^FluidProxyUgen.kr(\FluidBufChromaTrigger,-1, source, startFrame, numFrames, startChan, numChans, features, padding, numChroma, numChroma, ref, normalize, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufChroma: Invalid source buffer".throw}; - features.isNil.if {"FluidBufChroma: Invalid features buffer".throw}; + source.isNil.if {"FluidBufChroma: Invalid source buffer".throw}; + features.isNil.if {"FluidBufChroma: Invalid features buffer".throw}; ^this.new( server, nil, [features] ).processList( [source, startFrame, numFrames, startChan, numChans, features, padding, numChroma, numChroma, ref, normalize, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize, 0],freeWhenDone,action - ); + ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufChroma: Invalid source buffer".throw}; - features.isNil.if {"FluidBufChroma: Invalid features buffer".throw}; + source.isNil.if {"FluidBufChroma: Invalid source buffer".throw}; + features.isNil.if {"FluidBufChroma: Invalid features buffer".throw}; ^this.new( server, nil, [features] ).processList( [source, startFrame, numFrames, startChan, numChans, features, padding, numChroma, numChroma, ref, normalize, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize, 1],freeWhenDone,action - ); + ); } } FluidBufChromaTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufCompose.sc b/release-packaging/Classes/FluidBufCompose.sc index d5d8cc5..c4f904a 100644 --- a/release-packaging/Classes/FluidBufCompose.sc +++ b/release-packaging/Classes/FluidBufCompose.sc @@ -1,39 +1,39 @@ FluidBufCompose : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking = 1| - - source = source.asUGenInput; - destination = destination.asUGenInput; + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking = 1| - source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; + source = source.asUGenInput; + destination = destination.asUGenInput; - ^FluidProxyUgen.kr(\FluidBufComposeTrigger,-1, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking); + source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufComposeTrigger,-1, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action| - - source = source.asUGenInput; - destination = destination.asUGenInput; - - source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; - - ^this.new( server, nil, [destination]).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);//NB always blocking + + source = source.asUGenInput; + destination = destination.asUGenInput; + + source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; + + ^this.new( server, nil, [destination]).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);//NB always blocking } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; - source = source.asUGenInput; - destination = destination.asUGenInput; + source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; - source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; - - ^this.new( - server, nil, [destination] - ).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action); + ^this.new( + server, nil, [destination] + ).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action); } } FluidBufComposeTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufFlatten.sc b/release-packaging/Classes/FluidBufFlatten.sc index e204ac1..e2bab6d 100644 --- a/release-packaging/Classes/FluidBufFlatten.sc +++ b/release-packaging/Classes/FluidBufFlatten.sc @@ -1,47 +1,47 @@ FluidBufFlatten : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, trig = 1, blocking = 1| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, trig = 1, blocking = 1| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; - ^FluidProxyUgen.kr(\FluidBufFlattenTrigger,-1, source, startFrame, numFrames, startChan, numChans, destination, axis, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufFlattenTrigger,-1, source, startFrame, numFrames, startChan, numChans, destination, axis, trig, blocking); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; - ^this.new( - server, nil, [destination], + ^this.new( + server, nil, [destination], ).processList( [source, startFrame, numFrames, startChan, numChans, destination, axis,0],freeWhenDone,action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; - ^this.new( - server, nil, [destination], - ).processList( - [source, startFrame, numFrames, startChan, numChans, destination, axis,1],freeWhenDone,action - ); + ^this.new( + server, nil, [destination], + ).processList( + [source, startFrame, numFrames, startChan, numChans, destination, axis,1],freeWhenDone,action + ); - } + } } FluidBufFlattenTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufHPSS.sc b/release-packaging/Classes/FluidBufHPSS.sc index 5ec4f9d..1799d15 100644 --- a/release-packaging/Classes/FluidBufHPSS.sc +++ b/release-packaging/Classes/FluidBufHPSS.sc @@ -2,25 +2,25 @@ FluidBufHPSS : FluidBufProcessor { *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - harmonic = harmonic ? -1; - percussive = percussive ? -1; - residual = residual ? -1; - source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; + harmonic = harmonic ? -1; + percussive = percussive ? -1; + residual = residual ? -1; + source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; - ^FluidProxyUgen.kr(\FluidBufHPSSTrigger, -1, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking - ); + ^FluidProxyUgen.kr(\FluidBufHPSSTrigger, -1, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking + ); } - *process {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action| + *process {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - harmonic = harmonic ? -1; - percussive = percussive ? -1; - residual = residual ? -1; - source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; + harmonic = harmonic ? -1; + percussive = percussive ? -1; + residual = residual ? -1; + source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; ^this.new( @@ -31,22 +31,22 @@ FluidBufHPSS : FluidBufProcessor { } - *processBlocking {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action| + *processBlocking {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - harmonic = harmonic ? -1; - percussive = percussive ? -1; - residual = residual ? -1; - source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; + harmonic = harmonic ? -1; + percussive = percussive ? -1; + residual = residual ? -1; + source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; - ^this.new( - server, nil, [harmonic, percussive, residual].select{|x| x!= -1} - ).processList( - [source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone,action - ); + ^this.new( + server, nil, [harmonic, percussive, residual].select{|x| x!= -1} + ).processList( + [source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone,action + ); - } + } } FluidBufHPSSTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufLoudness.sc b/release-packaging/Classes/FluidBufLoudness.sc index 611c228..2b1bd19 100644 --- a/release-packaging/Classes/FluidBufLoudness.sc +++ b/release-packaging/Classes/FluidBufLoudness.sc @@ -1,76 +1,76 @@ FluidBufLoudness : FluidBufProcessor{ - - const (1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; - }; - bits = a.collect{ |sym| - (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) - }.reduce{|x,y| x | y}; - ^bits - } - - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, trig = 1, blocking = 0| - - var maxwindowSize = windowSize.nextPowerOfTwo; - - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - - source = source.asUGenInput; - features = features.asUGenInput; - - source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; - features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; - - ^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking); - } - - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action| - - var maxwindowSize = windowSize.nextPowerOfTwo; - - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - - source = source.asUGenInput; - features = features.asUGenInput; - - source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; - features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; + + const (1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; + }; + bits = a.collect{ |sym| + (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) + }.reduce{|x,y| x | y}; + ^bits + } + + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, trig = 1, blocking = 0| + + var maxwindowSize = windowSize.nextPowerOfTwo; + + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; + + ^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking); + } + + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action| + + var maxwindowSize = windowSize.nextPowerOfTwo; + + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^this.new( server, nil, [features] ).processList( [source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,0],freeWhenDone,action ); - } + } + + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action| - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action| + var maxwindowSize = windowSize.nextPowerOfTwo; - var maxwindowSize = windowSize.nextPowerOfTwo; - - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; - features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; + source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw}; + features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw}; ^this.new( server, nil, [features] ).processList( [source, startFrame, numFrames, startChan, numChans, features,padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,1],freeWhenDone,action ); - } + } } FluidBufLoudnessTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index 6ab8fb1..a3791d9 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -1,46 +1,46 @@ FluidBufMFCC : FluidBufProcessor{ - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; - ^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking); - } + ^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking); + } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action | + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action | - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; ^this.new( server, nil,[features] ).processList( - [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,0],freeWhenDone,action + [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,0],freeWhenDone,action ); - } + } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action | + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action | - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; ^this.new( server, nil,[features] ).processList( - [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,1],freeWhenDone,action + [source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,1],freeWhenDone,action ); - } + } } FluidBufMFCCTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufMelBands.sc b/release-packaging/Classes/FluidBufMelBands.sc index 1d2f8d9..f8618a7 100644 --- a/release-packaging/Classes/FluidBufMelBands.sc +++ b/release-packaging/Classes/FluidBufMelBands.sc @@ -1,51 +1,51 @@ FluidBufMelBands : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; + source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; ^FluidProxyUgen.kr(\FluidBufMelBandsTrigger,-1, source, startFrame, numFrames, startChan, numChans, features, padding, numBands, numBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; + source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; ^this.new( server, nil, [features] ).processList( [source, startFrame, numFrames, startChan, numChans, features, padding, numBands, numBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize, 0],freeWhenDone,action - ); + ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; + source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; ^this.new( server, nil, [features] ).processList( [source, startFrame, numFrames, startChan, numChans, features, padding, numBands, numBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize, 1],freeWhenDone,action - ); + ); } } FluidBufMelBandsTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufNMF.sc b/release-packaging/Classes/FluidBufNMF.sc index 3ff00a2..746620a 100644 --- a/release-packaging/Classes/FluidBufNMF.sc +++ b/release-packaging/Classes/FluidBufNMF.sc @@ -1,37 +1,37 @@ FluidBufNMF : FluidBufProcessor { - *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, resynthMode = 0, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| + *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, resynthMode = 0, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; - resynth = resynth ? -1; - bases = bases ? -1; - activations = activations ? -1; + source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; + resynth = resynth ? -1; + bases = bases ? -1; + activations = activations ? -1; - ^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1,source.asUGenInput, startFrame, numFrames, startChan, numChans, resynth.asUGenInput, resynthMode, bases.asUGenInput, basesMode, activations.asUGenInput, actMode, components, iterations, windowSize, hopSize, fftSize, fftSize, trig, blocking); - } + ^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1,source.asUGenInput, startFrame, numFrames, startChan, numChans, resynth.asUGenInput, resynthMode, bases.asUGenInput, basesMode, activations.asUGenInput, actMode, components, iterations, windowSize, hopSize, fftSize, fftSize, trig, blocking); + } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action| - source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; - resynth = resynth ? -1; - bases = bases ? -1; - activations = activations ? -1; + source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; + resynth = resynth ? -1; + bases = bases ? -1; + activations = activations ? -1; - ^this.new( - server,nil,[resynth, bases, activations].select{|x| x!= -1} - ).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 0],freeWhenDone,action); - } + ^this.new( + server,nil,[resynth, bases, activations].select{|x| x!= -1} + ).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 0],freeWhenDone,action); + } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action| - source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; - resynth = resynth ? -1; - bases = bases ? -1; - activations = activations ? -1; + source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; + resynth = resynth ? -1; + bases = bases ? -1; + activations = activations ? -1; - ^this.new( - server,nil,[resynth, bases, activations].select{|x| x!= -1} - ).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 1],freeWhenDone,action); - } + ^this.new( + server,nil,[resynth, bases, activations].select{|x| x!= -1} + ).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 1],freeWhenDone,action); + } } FluidBufNMFTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufNMFCross.sc b/release-packaging/Classes/FluidBufNMFCross.sc index 2312f62..b9fa970 100644 --- a/release-packaging/Classes/FluidBufNMFCross.sc +++ b/release-packaging/Classes/FluidBufNMFCross.sc @@ -1,45 +1,45 @@ FluidBufNMFCross : FluidBufProcessor { - *kr { |source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| + *kr { |source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - source = source.asUGenInput; - target = target.asUGenInput; - output = output.asUGenInput; - source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; - target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; - output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; + source = source.asUGenInput; + target = target.asUGenInput; + output = output.asUGenInput; + source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; + target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; + output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; ^FluidProxyUgen.kr(\FluidBufNMFCrossTrigger, -1, source, target, output, timeSparsity, polyphony, continuity, iterations, windowSize, hopSize, fftSize, fftSize, trig, blocking); } - *process { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *process { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - source = source.asUGenInput; - target = target.asUGenInput; - output = output.asUGenInput; - source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; - target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; - output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; + source = source.asUGenInput; + target = target.asUGenInput; + output = output.asUGenInput; + source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; + target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; + output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; - ^this.new( + ^this.new( server, nil, [output] ).processList( [source, target, output, timeSparsity, polyphony, continuity, iterations, windowSize, hopSize, fftSize, fftSize, 0],freeWhenDone, action ); } - *processBlocking { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *processBlocking { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - source = source.asUGenInput; - target = target.asUGenInput; - output = output.asUGenInput; - source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; - target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; - output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; + source = source.asUGenInput; + target = target.asUGenInput; + output = output.asUGenInput; + source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; + target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; + output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; - ^this.new( + ^this.new( server, nil, [output] ).processList( [source, target, output, timeSparsity, polyphony, continuity, iterations, windowSize, hopSize, fftSize, fftSize, 1],freeWhenDone, action diff --git a/release-packaging/Classes/FluidBufNMFSeed.sc b/release-packaging/Classes/FluidBufNMFSeed.sc index dca1fcd..177b4cb 100644 --- a/release-packaging/Classes/FluidBufNMFSeed.sc +++ b/release-packaging/Classes/FluidBufNMFSeed.sc @@ -2,12 +2,12 @@ FluidBufNMFSeed : FluidBufProcessor{ *kr { |source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw}; - bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; - activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; - source = source.asUGenInput; - bases = bases.asUGenInput; - activations = activations.asUGenInput; + source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw}; + bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; + activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; + source = source.asUGenInput; + bases = bases.asUGenInput; + activations = activations.asUGenInput; ^FluidProxyUgen.kr1(\FluidBufNMFSeedTrigger, -1, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, fftSize, trig, blocking); } @@ -15,12 +15,12 @@ FluidBufNMFSeed : FluidBufProcessor{ *process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw}; - bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; - activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; - source = source.asUGenInput; - bases = bases.asUGenInput; - activations = activations.asUGenInput; + source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw}; + bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; + activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; + source = source.asUGenInput; + bases = bases.asUGenInput; + activations = activations.asUGenInput; ^this.new( server, nil, [bases,activations] @@ -29,14 +29,14 @@ FluidBufNMFSeed : FluidBufProcessor{ ) } - *processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw}; - bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; - activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; - source = source.asUGenInput; - bases = bases.asUGenInput; - activations = activations.asUGenInput; + source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw}; + bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; + activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw}; + source = source.asUGenInput; + bases = bases.asUGenInput; + activations = activations.asUGenInput; ^this.new( server, nil, [bases,activations] diff --git a/release-packaging/Classes/FluidBufNoveltyFeature.sc b/release-packaging/Classes/FluidBufNoveltyFeature.sc index 1fca9ac..413ebd7 100644 --- a/release-packaging/Classes/FluidBufNoveltyFeature.sc +++ b/release-packaging/Classes/FluidBufNoveltyFeature.sc @@ -1,8 +1,8 @@ FluidBufNoveltyFeature : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; features = features.asUGenInput; @@ -18,20 +18,20 @@ FluidBufNoveltyFeature : FluidBufProcessor { } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action | + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action | - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm); if (algorithm.isNil or: algorithm.isUGen) { ("FluidBufNoveltySlice: % is not a recognised algorithm") .format(algorithm).throw; }; - source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw}; + source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw}; ^this.new( server, nil, [features] @@ -40,15 +40,15 @@ FluidBufNoveltyFeature : FluidBufProcessor { ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action | + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action | - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw}; + source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw}; algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm); if (algorithm.isNil or: algorithm.isUGen) { ("FluidBufNoveltySlice: % is not a recognised algorithm") diff --git a/release-packaging/Classes/FluidBufNoveltySlice.sc b/release-packaging/Classes/FluidBufNoveltySlice.sc index 82952fe..050241e 100644 --- a/release-packaging/Classes/FluidBufNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufNoveltySlice.sc @@ -2,7 +2,7 @@ FluidBufNoveltySlice : FluidBufProcessor { *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1 , blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; indices = indices.asUGenInput; @@ -18,20 +18,20 @@ FluidBufNoveltySlice : FluidBufProcessor { } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action | + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action | - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - indices = indices.asUGenInput; - algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm); - if (algorithm.isNil or: algorithm.isUGen) { - ("FluidBufNoveltySlice: % is not a recognised algorithm") - .format(algorithm).throw; - }; + source = source.asUGenInput; + indices = indices.asUGenInput; + algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm); + if (algorithm.isNil or: algorithm.isUGen) { + ("FluidBufNoveltySlice: % is not a recognised algorithm") + .format(algorithm).throw; + }; - source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; ^this.new( server, nil, [indices] @@ -40,20 +40,20 @@ FluidBufNoveltySlice : FluidBufProcessor { ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action | + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action | - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - indices = indices.asUGenInput; - algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm); - if (algorithm.isNil or: algorithm.isUGen) { - ("FluidBufNoveltySlice: % is not a recognised algorithm") - .format(algorithm).throw; - }; + source = source.asUGenInput; + indices = indices.asUGenInput; + algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm); + if (algorithm.isNil or: algorithm.isUGen) { + ("FluidBufNoveltySlice: % is not a recognised algorithm") + .format(algorithm).throw; + }; - source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; ^this.new( server, nil, [indices] diff --git a/release-packaging/Classes/FluidBufOnsetFeature.sc b/release-packaging/Classes/FluidBufOnsetFeature.sc index 1b3235c..f8217bf 100644 --- a/release-packaging/Classes/FluidBufOnsetFeature.sc +++ b/release-packaging/Classes/FluidBufOnsetFeature.sc @@ -1,35 +1,35 @@ FluidBufOnsetFeature : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; - metric = FluidOnsetSlice.prSelectMetric(metric) ?? { - ("FluidBufOnsetSlice: % is not a recognised metric") - .format(metric).throw; - }; + source = source.asUGenInput; + features = features.asUGenInput; + metric = FluidOnsetSlice.prSelectMetric(metric) ?? { + ("FluidBufOnsetSlice: % is not a recognised metric") + .format(metric).throw; + }; - source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw}; + source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw}; ^FluidProxyUgen.kr(\FluidBufOnsetFeatureTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, metric, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; - metric = FluidOnsetSlice.prSelectMetric(metric); - if (metric.isNil or: metric.isUGen) { - ("FluidBufOnsetSlice: % is not a recognised metric") - .format(metric).throw; - }; + source = source.asUGenInput; + features = features.asUGenInput; + metric = FluidOnsetSlice.prSelectMetric(metric); + if (metric.isNil or: metric.isUGen) { + ("FluidBufOnsetSlice: % is not a recognised metric") + .format(metric).throw; + }; - source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw}; + source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw}; ^this.new( server, nil, [features] @@ -38,20 +38,20 @@ FluidBufOnsetFeature : FluidBufProcessor { ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1,padding = 1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1,padding = 1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - features = features.asUGenInput; - metric = FluidOnsetSlice.prSelectMetric(metric); - if (metric.isNil or: metric.isUGen) { - ("FluidBufOnsetSlice: % is not a recognised metric") - .format(metric).throw; - }; + source = source.asUGenInput; + features = features.asUGenInput; + metric = FluidOnsetSlice.prSelectMetric(metric); + if (metric.isNil or: metric.isUGen) { + ("FluidBufOnsetSlice: % is not a recognised metric") + .format(metric).throw; + }; - source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw}; - features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw}; + source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw}; + features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw}; ^this.new( server, nil, [features] diff --git a/release-packaging/Classes/FluidBufOnsetSlice.sc b/release-packaging/Classes/FluidBufOnsetSlice.sc index 0a3f6d7..992f6e5 100644 --- a/release-packaging/Classes/FluidBufOnsetSlice.sc +++ b/release-packaging/Classes/FluidBufOnsetSlice.sc @@ -1,36 +1,36 @@ FluidBufOnsetSlice : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - indices = indices.asUGenInput; - metric = FluidOnsetSlice.prSelectMetric(metric) ?? { - ("FluidBufOnsetSlice: % is not a recognised metric") - .format(metric).throw; - }; + source = source.asUGenInput; + indices = indices.asUGenInput; + metric = FluidOnsetSlice.prSelectMetric(metric) ?? { + ("FluidBufOnsetSlice: % is not a recognised metric") + .format(metric).throw; + }; - source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; + source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; ^FluidProxyUgen.kr(\FluidBufOnsetSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - indices = indices.asUGenInput; - metric = FluidOnsetSlice.prSelectMetric(metric); - if (metric.isNil or: metric.isUGen) { - ("FluidBufOnsetSlice: % is not a recognised metric") - .format(metric).throw; - }; + source = source.asUGenInput; + indices = indices.asUGenInput; + metric = FluidOnsetSlice.prSelectMetric(metric); + if (metric.isNil or: metric.isUGen) { + ("FluidBufOnsetSlice: % is not a recognised metric") + .format(metric).throw; + }; - source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; + source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; ^this.new( server, nil, [indices] @@ -39,20 +39,20 @@ FluidBufOnsetSlice : FluidBufProcessor { ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - indices = indices.asUGenInput; - metric = FluidOnsetSlice.prSelectMetric(metric); - if (metric.isNil or: metric.isUGen) { - ("FluidBufOnsetSlice: % is not a recognised metric") - .format(metric).throw; - }; + source = source.asUGenInput; + indices = indices.asUGenInput; + metric = FluidOnsetSlice.prSelectMetric(metric); + if (metric.isNil or: metric.isUGen) { + ("FluidBufOnsetSlice: % is not a recognised metric") + .format(metric).throw; + }; - source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; + source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; ^this.new( server, nil, [indices] diff --git a/release-packaging/Classes/FluidBufPitch.sc b/release-packaging/Classes/FluidBufPitch.sc index 86a0cb1..329ecb4 100644 --- a/release-packaging/Classes/FluidBufPitch.sc +++ b/release-packaging/Classes/FluidBufPitch.sc @@ -1,52 +1,52 @@ FluidBufPitch : FluidBufProcessor{ - const (1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; - }; - bits = a.collect{ |sym| - (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) - }.reduce{|x,y| x | y}; - ^bits - } + const (1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; + }; + bits = a.collect{ |sym| + (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) + }.reduce{|x,y| x | y}; + ^bits + } *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; - features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; - ^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; - features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; ^this.new( server, nil, [features] @@ -55,17 +55,17 @@ FluidBufPitch : FluidBufProcessor{ ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - source = source.asUGenInput; - features = features.asUGenInput; + source = source.asUGenInput; + features = features.asUGenInput; - source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; - features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; ^this.new( server, nil, [features] diff --git a/release-packaging/Classes/FluidBufSTFT.sc b/release-packaging/Classes/FluidBufSTFT.sc index 0c2aa7b..7216dee 100644 --- a/release-packaging/Classes/FluidBufSTFT.sc +++ b/release-packaging/Classes/FluidBufSTFT.sc @@ -1,38 +1,38 @@ FluidBufSTFT : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0,windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 1| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0,windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 1| - source = source ? -1; - magnitude = magnitude ? -1; - phase = phase ? -1; - resynth = resynth ? - 1; + source = source ? -1; + magnitude = magnitude ? -1; + phase = phase ? -1; + resynth = resynth ? - 1; - ^FluidProxyUgen.kr(\FluidBufSTFTTrigger, -1, source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufSTFTTrigger, -1, source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, trig, blocking); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| - source = source ? -1; - magnitude = magnitude ? -1; - phase = phase ? -1; - resynth = resynth ? - 1; + source = source ? -1; + magnitude = magnitude ? -1; + phase = phase ? -1; + resynth = resynth ? - 1; - ^this.new( - server, nil, [magnitude,phase,resynth].select{|b| b != -1} + ^this.new( + server, nil, [magnitude,phase,resynth].select{|b| b != -1} ).processList( [source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1,freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1,freeWhenDone = true, action| - source = source ? -1; - magnitude = magnitude ? -1; - phase = phase ? -1; - resynth = resynth ? - 1; + source = source ? -1; + magnitude = magnitude ? -1; + phase = phase ? -1; + resynth = resynth ? - 1; - ^this.new( - server, nil, [magnitude,phase,resynth].select{|b| b != -1} + ^this.new( + server, nil, [magnitude,phase,resynth].select{|b| b != -1} ).processList( [source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, 1], freeWhenDone, action ); diff --git a/release-packaging/Classes/FluidBufScale.sc b/release-packaging/Classes/FluidBufScale.sc index 7c9f449..9b3035f 100644 --- a/release-packaging/Classes/FluidBufScale.sc +++ b/release-packaging/Classes/FluidBufScale.sc @@ -1,41 +1,41 @@ FluidBufScale : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, trig = 1, blocking = 1| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, trig = 1, blocking = 1| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; - ^FluidProxyUgen.kr(\FluidBufScaleTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufScaleTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, trig, blocking); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; - ^this.new( - server, nil, [destination] + ^this.new( + server, nil, [destination] ).processList( [source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; - ^this.new( - server, nil, [destination] + ^this.new( + server, nil, [destination] ).processList( [source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, 1], freeWhenDone, action ); diff --git a/release-packaging/Classes/FluidBufSelect.sc b/release-packaging/Classes/FluidBufSelect.sc index ff0a9d7..c918155 100644 --- a/release-packaging/Classes/FluidBufSelect.sc +++ b/release-packaging/Classes/FluidBufSelect.sc @@ -1,62 +1,62 @@ FluidBufSelect : FluidBufProcessor { - *kr { |source, destination, indices=#[-1], channels=#[-1], trig = 1, blocking = 1| + *kr { |source, destination, indices=#[-1], channels=#[-1], trig = 1, blocking = 1| - var params; + var params; - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - indices = indices.asArray; - channels = channels.asArray; + indices = indices.asArray; + channels = channels.asArray; - indices = [indices.size] ++ indices; - channels = [channels.size] ++ channels; + indices = [indices.size] ++ indices; + channels = [channels.size] ++ channels; - source.isNil.if {"FluidBufSelect: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufSelect: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw}; - params = indices ++ channels ++ [trig, blocking] + params = indices ++ channels ++ [trig, blocking] - ^FluidProxyUgen.kr(\FluidBufSelectTrigger,-1, source, destination, *params); + ^FluidProxyUgen.kr(\FluidBufSelectTrigger,-1, source, destination, *params); } *process { |server, source, destination, indices=#[-1], channels=#[-1], freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufSelect: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufSelect: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw}; - indices = indices.asArray; - channels = channels.asArray; + indices = indices.asArray; + channels = channels.asArray; indices = [indices.size] ++ indices; - channels = [channels.size] ++ channels; + channels = [channels.size] ++ channels; - ^this.new(server, nil, [destination]).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking + ^this.new(server, nil, [destination]).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking } - *processBlocking { |server, source, destination, indices=#[-1], channels=#[-1], freeWhenDone = true, action| + *processBlocking { |server, source, destination, indices=#[-1], channels=#[-1], freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufSelect: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufSelect: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw}; - indices = indices.asArray; - channels = channels.asArray; + indices = indices.asArray; + channels = channels.asArray; - indices = [indices.size] ++ indices; - channels = [channels.size] ++ channels; + indices = [indices.size] ++ indices; + channels = [channels.size] ++ channels; - ^this.new( - server, nil, [destination] - ).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking + ^this.new( + server, nil, [destination] + ).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking } } FluidBufSelectTrigger : FluidProxyUgen {} diff --git a/release-packaging/Classes/FluidBufSelectEvery.sc b/release-packaging/Classes/FluidBufSelectEvery.sc index 1c12f60..3dfbb2a 100644 --- a/release-packaging/Classes/FluidBufSelectEvery.sc +++ b/release-packaging/Classes/FluidBufSelectEvery.sc @@ -1,41 +1,41 @@ FluidBufSelectEvery : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, trig = 1, blocking = 1| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, trig = 1, blocking = 1| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw}; - ^FluidProxyUgen.kr(\FluidBufSelectEveryTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufSelectEveryTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, trig, blocking); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw}; - ^this.new( - server, nil, [destination] + ^this.new( + server, nil, [destination] ).processList( [source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, freeWhenDone = true, action| - source = source.asUGenInput; - destination = destination.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw}; + source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw}; - ^this.new( - server, nil, [destination] + ^this.new( + server, nil, [destination] ).processList( [source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, 1], freeWhenDone, action ); diff --git a/release-packaging/Classes/FluidBufSines.sc b/release-packaging/Classes/FluidBufSines.sc index 781ed64..9fa48e1 100644 --- a/release-packaging/Classes/FluidBufSines.sc +++ b/release-packaging/Classes/FluidBufSines.sc @@ -1,27 +1,27 @@ FluidBufSines : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - sines = sines !? {sines.asUGenInput} ?? {-1}; - residual = residual !? {residual.asUGenInput} ?? {-1}; + source = source.asUGenInput; + sines = sines !? {sines.asUGenInput} ?? {-1}; + residual = residual !? {residual.asUGenInput} ?? {-1}; - source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; + source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; ^FluidProxyUgen.multiNew(\FluidBufSinesTrigger, -1, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - sines = sines !? {sines.asUGenInput} ?? {-1}; - residual = residual !? {residual.asUGenInput} ?? {-1}; + source = source.asUGenInput; + sines = sines !? {sines.asUGenInput} ?? {-1}; + residual = residual !? {residual.asUGenInput} ?? {-1}; - source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; + source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; ^this.new( server, nil, [sines, residual].select{|x| x!= -1} @@ -30,15 +30,15 @@ FluidBufSines : FluidBufProcessor { ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - sines = sines !? {sines.asUGenInput} ?? {-1}; - residual = residual !? {residual.asUGenInput} ?? {-1}; + source = source.asUGenInput; + sines = sines !? {sines.asUGenInput} ?? {-1}; + residual = residual !? {residual.asUGenInput} ?? {-1}; - source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; + source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; ^this.new( server, nil, [sines, residual].select{|x| x!= -1} diff --git a/release-packaging/Classes/FluidBufSpectralShape.sc b/release-packaging/Classes/FluidBufSpectralShape.sc index 6ef0b68..5942387 100644 --- a/release-packaging/Classes/FluidBufSpectralShape.sc +++ b/release-packaging/Classes/FluidBufSpectralShape.sc @@ -1,9 +1,9 @@ FluidBufSpectralShape : FluidBufProcessor { - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)}; + var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)}; source = source.asUGenInput; features = features.asUGenInput; @@ -16,10 +16,10 @@ FluidBufSpectralShape : FluidBufProcessor { } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)}; + var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)}; source = source.asUGenInput; features = features.asUGenInput; @@ -30,14 +30,14 @@ FluidBufSpectralShape : FluidBufProcessor { ^this.new( server, nil, [features] ).processList( - [source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action + [source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)}; + var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)}; source = source.asUGenInput; features = features.asUGenInput; @@ -48,7 +48,7 @@ FluidBufSpectralShape : FluidBufProcessor { ^this.new( server, nil, [features] ).processList( - [source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action + [source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action ); } } diff --git a/release-packaging/Classes/FluidBufStats.sc b/release-packaging/Classes/FluidBufStats.sc index e0875fb..5db6176 100644 --- a/release-packaging/Classes/FluidBufStats.sc +++ b/release-packaging/Classes/FluidBufStats.sc @@ -1,49 +1,49 @@ FluidBufStats : FluidBufProcessor { - const 1) { ("Option '" ++ item ++ "' is repeated").warn}; - }; - bits = a.collect{ |sym| - (statslookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) - }.reduce{|x,y| x | y}; - ^bits - } - - *initClass { - statslookup = Dictionary.with(*this.stats.collect{|x,i| x->(1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; + }; + bits = a.collect{ |sym| + (statslookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) + }.reduce{|x,y| x | y}; + ^bits + } + + *initClass { + statslookup = Dictionary.with(*this.stats.collect{|x,i| x->(1<oversample, <>extent, <>axis; *new {|server, oversample = 1, extent = 0, axis = 0| ^super.new(server,[oversample, extent, axis]) - .oversample_(oversample).extent_(extent).axis_(axis); + .oversample_(oversample).extent_(extent).axis_(axis); } - prGetParams{ - ^[this.oversample, this.extent, this.axis]; - } + prGetParams{ + ^[this.oversample, this.extent, this.axis]; + } fitTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); - } + ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); + } fitTransform{|sourceDataSet, destDataSet, action| - actions[\fitTransform] = [nil,action]; - this.fitTransformMsg(sourceDataSet,destDataSet); + actions[\fitTransform] = [nil,action]; + this.fitTransformMsg(sourceDataSet,destDataSet); this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); } diff --git a/release-packaging/Classes/FluidHPSS.sc b/release-packaging/Classes/FluidHPSS.sc index a2960e5..64cf83b 100644 --- a/release-packaging/Classes/FluidHPSS.sc +++ b/release-packaging/Classes/FluidHPSS.sc @@ -1,9 +1,9 @@ FluidHPSS : FluidRTMultiOutUGen { *ar { arg in = 0, harmFilterSize=17, percFilterSize = 31, maskingMode=0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = -1, maxHarmFilterSize, maxPercFilterSize; - - maxHarmFilterSize = maxHarmFilterSize ? harmFilterSize; - maxPercFilterSize = maxPercFilterSize ? percFilterSize; - + + maxHarmFilterSize = maxHarmFilterSize ? harmFilterSize; + maxPercFilterSize = maxPercFilterSize ? percFilterSize; + ^this.multiNew('audio', in.asAudioRateInput(this), harmFilterSize, maxHarmFilterSize, percFilterSize, maxPercFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize) } init { arg ... theInputs; @@ -15,16 +15,16 @@ FluidHPSS : FluidRTMultiOutUGen { ]; ^channels } - checkInputs { + checkInputs { if(inputs.at(17).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); - }; + }; if(inputs.at(2).rate != 'scalar') { ^(": maxHarmFilterSize cannot be modulated."); - }; + }; if(inputs.at(4).rate != 'scalar') { ^(": maxPercFilterSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index 3dc3c04..2f688a7 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -1,69 +1,69 @@ FluidKDTree : FluidModelObject - { +{ - var neighbours,radius; + var neighbours,radius; - *new{ |server, numNeighbours = 1, radius = 0| - ^super.new(server,[numNeighbours,radius ? -1]) - .numNeighbours_(numNeighbours) - .radius_(radius); - } + *new{ |server, numNeighbours = 1, radius = 0| + ^super.new(server,[numNeighbours,radius ? -1]) + .numNeighbours_(numNeighbours) + .radius_(radius); + } - numNeighbours_{|k|neighbours = k.asInteger; } - numNeighbours{ ^neighbours; } + numNeighbours_{|k|neighbours = k.asInteger; } + numNeighbours{ ^neighbours; } - radius_{|r| radius = r.asUGenInput;} - radius{ ^radius; } + radius_{|r| radius = r.asUGenInput;} + radius{ ^radius; } - prGetParams{^[this.id, this.numNeighbours,this.radius];} + prGetParams{^[this.id, this.numNeighbours,this.radius];} - fitMsg{ |dataSet| ^this.prMakeMsg(\fit,this.id,dataSet.id);} + fitMsg{ |dataSet| ^this.prMakeMsg(\fit,this.id,dataSet.id);} fit{|dataSet,action| - actions[\fit] = [nil,action]; + actions[\fit] = [nil,action]; this.prSendMsg(this.fitMsg(dataSet)); } - kNearestMsg{|buffer,k| - k !? - {^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer),k);} - ?? - {^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer));} - } + kNearestMsg{|buffer,k| + k !? + {^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer),k);} + ?? + {^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer));} + } kNearest{ |buffer, k, action| - actions[\kNearest] = [strings(FluidMessageResponse,_,_),action]; + actions[\kNearest] = [strings(FluidMessageResponse,_,_),action]; this.prSendMsg(this.kNearestMsg(buffer,k)); } - kNearestDistMsg {|buffer| - ^this.prMakeMsg(\kNearestDist,id,this.prEncodeBuffer(buffer)); - } + kNearestDistMsg {|buffer| + ^this.prMakeMsg(\kNearestDist,id,this.prEncodeBuffer(buffer)); + } kNearestDist { |buffer, action| - actions[\kNearestDist] = [numbers(FluidMessageResponse,_,nil,_),action]; + actions[\kNearestDist] = [numbers(FluidMessageResponse,_,nil,_),action]; this.prSendMsg(this.kNearestDistMsg(buffer)); } - kr{|trig, inputBuffer,outputBuffer, numNeighbours = 1, lookupDataSet| -/* this.numNeighbours_(numNeighbours); - lookupDataSet = lookupDataSet ? -1; - this.lookupDataSet_(lookupDataSet);*/ + kr{|trig, inputBuffer,outputBuffer, numNeighbours = 1, lookupDataSet| + /* this.numNeighbours_(numNeighbours); + lookupDataSet = lookupDataSet ? -1; + this.lookupDataSet_(lookupDataSet);*/ - ^FluidKDTreeQuery.kr(trig, - this, numNeighbours, this.radius,lookupDataSet.asUGenInput, - inputBuffer,outputBuffer); - } + ^FluidKDTreeQuery.kr(trig, + this, numNeighbours, this.radius,lookupDataSet.asUGenInput, + inputBuffer,outputBuffer); + } } FluidKDTreeQuery : FluidRTMultiOutUGen { - *kr{ |trig, tree, numNeighbours, radius,lookupDataSet, inputBuffer, outputBuffer | - ^this.multiNew('control',trig, tree.asUGenInput, numNeighbours, radius,lookupDataSet!?(_.asUGenInput)??{-1}, inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, tree, numNeighbours, radius,lookupDataSet, inputBuffer, outputBuffer | + ^this.multiNew('control',trig, tree.asUGenInput, numNeighbours, radius,lookupDataSet!?(_.asUGenInput)??{-1}, inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index ca0a828..008fe99 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -80,9 +80,9 @@ FluidKMeans : FluidModelObject { transformPointMsg{ |sourceBuffer, targetBuffer| ^this.prMakeMsg(\transformPoint, id, this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(targetBuffer), - ["/b_query", targetBuffer.asUGenInput]); - } + this.prEncodeBuffer(targetBuffer), + ["/b_query", targetBuffer.asUGenInput]); + } transformPoint { |sourceBuffer, targetBuffer, action| actions[\transformPoint] = [nil,{action.value(targetBuffer)}]; @@ -91,40 +91,40 @@ FluidKMeans : FluidModelObject { getMeansMsg{|dataSet| ^this.prMakeMsg(\getMeans, id, dataSet.asUGenInput) } - getMeans{ |dataSet, action| - actions[\getMeans] = [nil, action]; + getMeans{ |dataSet, action| + actions[\getMeans] = [nil, action]; this.prSendMsg(this.getMeansMsg(dataSet)); } setMeansMsg{|dataSet| ^this.prMakeMsg(\setMeans, id, dataSet.asUGenInput) } - setMeans{ |dataSet, action| - actions[\setMeans] = [nil, action]; + setMeans{ |dataSet, action| + actions[\setMeans] = [nil, action]; this.prSendMsg(this.setMeansMsg(dataSet)); } clearMsg{ ^this.prMakeMsg(\clear, id) } - clear{ |action| - actions[\clear] = [nil, action]; + clear{ |action| + actions[\clear] = [nil, action]; this.prSendMsg(this.clearMsg); } kr{|trig, inputBuffer,outputBuffer| - ^FluidKMeansQuery.kr(trig, - this, - this.prEncodeBuffer(inputBuffer), - this.prEncodeBuffer(outputBuffer)); + ^FluidKMeansQuery.kr(trig, + this, + this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)); } } FluidKMeansQuery : FluidRTMultiOutUGen { - *kr{ |trig, model,inputBuffer, outputBuffer | - ^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model,inputBuffer, outputBuffer | + ^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidKNNClassifier.sc b/release-packaging/Classes/FluidKNNClassifier.sc index 19f8767..b41b738 100644 --- a/release-packaging/Classes/FluidKNNClassifier.sc +++ b/release-packaging/Classes/FluidKNNClassifier.sc @@ -1,60 +1,60 @@ FluidKNNClassifier : FluidModelObject { - var <>numNeighbours, <>weight; + var <>numNeighbours, <>weight; *new {|server, numNeighbours = 3, weight = 1| ^super.new(server,[numNeighbours,weight]) - .numNeighbours_(numNeighbours) - .weight_(weight); + .numNeighbours_(numNeighbours) + .weight_(weight); } - prGetParams{^[this.id,this.numNeighbours,this.weight];} + prGetParams{^[this.id,this.numNeighbours,this.weight];} - fitMsg{|dataSet, labelSet| - ^this.prMakeMsg(\fit, id, dataSet.id, labelSet.id) - } + fitMsg{|dataSet, labelSet| + ^this.prMakeMsg(\fit, id, dataSet.id, labelSet.id) + } fit{|dataSet, labelSet, action| - actions[\fit] = [nil,action]; - this.prSendMsg(this.fitMsg(dataSet, labelSet)); + actions[\fit] = [nil,action]; + this.prSendMsg(this.fitMsg(dataSet, labelSet)); } - predictMsg{|dataSet, labelSet| - ^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id) - } + predictMsg{|dataSet, labelSet| + ^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id) + } - predict{|dataSet, labelSet, action| - actions[\predict] = [nil, action]; + predict{|dataSet, labelSet, action| + actions[\predict] = [nil, action]; this.prSendMsg(this.predictMsg(dataSet, labelSet)); } - predictPointMsg{|buffer| - ^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer)) - } + predictPointMsg{|buffer| + ^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer)) + } predictPoint {|buffer, action| actions[\predictPoint] = [string(FluidMessageResponse,_,_),action]; this.prSendMsg(this.predictPointMsg(buffer)); } - kr{|trig, inputBuffer,outputBuffer| - ^FluidKNNClassifierQuery.kr(trig, - this, this.numNeighbours, this.weight, - this.prEncodeBuffer(inputBuffer), - this.prEncodeBuffer(outputBuffer)); - } + kr{|trig, inputBuffer,outputBuffer| + ^FluidKNNClassifierQuery.kr(trig, + this, this.numNeighbours, this.weight, + this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)); + } } FluidKNNClassifierQuery : FluidRTMultiOutUGen { - *kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer | - ^this.multiNew('control',trig, model.asUGenInput, - numNeighbours,weight, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer | + ^this.multiNew('control',trig, model.asUGenInput, + numNeighbours,weight, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidKNNRegressor.sc b/release-packaging/Classes/FluidKNNRegressor.sc index b386e2e..992b67e 100644 --- a/release-packaging/Classes/FluidKNNRegressor.sc +++ b/release-packaging/Classes/FluidKNNRegressor.sc @@ -1,59 +1,59 @@ FluidKNNRegressor : FluidModelObject { - var <>numNeighbours, <>weight; + var <>numNeighbours, <>weight; *new {|server, numNeighbours = 3, weight = 1| ^super.new(server,[numNeighbours,weight]) - .numNeighbours_(numNeighbours) - .weight_(weight); + .numNeighbours_(numNeighbours) + .weight_(weight); } - prGetParams{^[this.id,this.numNeighbours,this.weight,-1,-1];} + prGetParams{^[this.id,this.numNeighbours,this.weight,-1,-1];} - fitMsg{|sourceDataSet, targetDataSet| - ^this.prMakeMsg(\fit,this.id,sourceDataSet.id,targetDataSet.id) - } + fitMsg{|sourceDataSet, targetDataSet| + ^this.prMakeMsg(\fit,this.id,sourceDataSet.id,targetDataSet.id) + } fit{|sourceDataSet, targetDataSet, action| - actions[\fit] = [nil,action]; - this.prSendMsg(this.fitMsg(sourceDataSet, targetDataSet)); + actions[\fit] = [nil,action]; + this.prSendMsg(this.fitMsg(sourceDataSet, targetDataSet)); } - predictMsg{ |sourceDataSet, targetDataSet| - ^this.prMakeMsg(\predict,this.id,sourceDataSet.id,targetDataSet.id) - } + predictMsg{ |sourceDataSet, targetDataSet| + ^this.prMakeMsg(\predict,this.id,sourceDataSet.id,targetDataSet.id) + } predict{ |sourceDataSet, targetDataSet,action| - actions[\predict] = [nil, action]; + actions[\predict] = [nil, action]; this.prSendMsg(this.predictMsg(sourceDataSet, targetDataSet)); } - predictPointMsg { |buffer| - ^this.prMakeMsg(\predictPoint,id, this.prEncodeBuffer(buffer)); - } + predictPointMsg { |buffer| + ^this.prMakeMsg(\predictPoint,id, this.prEncodeBuffer(buffer)); + } predictPoint { |buffer, action| actions[\predictPoint] = [number(FluidMessageResponse,_,_),action]; this.prSendMsg(this.predictPointMsg(buffer)); } - kr{|trig, inputBuffer,outputBuffer| - ^FluidKNNRegressorQuery.kr(K2A.ar(trig), - this, this.numNeighbours, this.weight, - this.prEncodeBuffer(inputBuffer), - this.prEncodeBuffer(outputBuffer)); - } + kr{|trig, inputBuffer,outputBuffer| + ^FluidKNNRegressorQuery.kr(K2A.ar(trig), + this, this.numNeighbours, this.weight, + this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)); + } } FluidKNNRegressorQuery : FluidRTMultiOutUGen { - *kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer | - ^this.multiNew('control',trig, model.asUGenInput, - numNeighbours,weight, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer | + ^this.multiNew('control',trig, model.asUGenInput, + numNeighbours,weight, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index c30cce8..c0e7064 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -1,19 +1,19 @@ FluidLabelSet : FluidDataObject { - *new{|server| ^super.new(server) } + *new{|server| ^super.new(server) } - addLabelMsg{|identifier,label| - ^this.prMakeMsg(\addLabel,id,identifier.asSymbol,label.asSymbol); - } + addLabelMsg{|identifier,label| + ^this.prMakeMsg(\addLabel,id,identifier.asSymbol,label.asSymbol); + } addLabel{|identifier, label, action| - actions[\addLabel] = [nil, action]; + actions[\addLabel] = [nil, action]; this.prSendMsg(this.addLabelMsg(identifier,label)); } - updateLabelMsg{|identifier, label| - ^this.prMakeMsg(\updateLabel, id, identifier.asSymbol, label.asSymbol); - } + updateLabelMsg{|identifier, label| + ^this.prMakeMsg(\updateLabel, id, identifier.asSymbol, label.asSymbol); + } setLabelMsg{|identifier,label| ^this.prMakeMsg(\setLabel,id,identifier.asSymbol,label.asSymbol); @@ -25,36 +25,36 @@ FluidLabelSet : FluidDataObject { } updateLabel{|identifier, label, action| - actions[\updateLabel] = [nil,action]; + actions[\updateLabel] = [nil,action]; this.prSendMsg(this.updateLabelMsg(identifier,label)); } - getLabelMsg{|identifier| - ^this.prMakeMsg(\getLabel, id, identifier.asSymbol); - } + getLabelMsg{|identifier| + ^this.prMakeMsg(\getLabel, id, identifier.asSymbol); + } getLabel{|identifier, action| - actions[\getLabel] = [string(FluidMessageResponse,_,_),action]; + actions[\getLabel] = [string(FluidMessageResponse,_,_),action]; this.prSendMsg(this.getLabelMsg(identifier)); } - deleteLabelMsg{|identifier, action| - ^this.prMakeMsg(\deleteLabel, id, identifier.asSymbol); - } + deleteLabelMsg{|identifier, action| + ^this.prMakeMsg(\deleteLabel, id, identifier.asSymbol); + } deleteLabel{|identifier, action| - actions[\deleteLabel] = [nil, action]; + actions[\deleteLabel] = [nil, action]; this.prSendMsg(this.deleteLabelMsg(identifier)); } - clearMsg { ^this.prMakeMsg(\clear,id); } + clearMsg { ^this.prMakeMsg(\clear,id); } - clear { |action| - actions[\clear] = [nil,action]; - this.prSendMsg(this.clearMsg); - } + clear { |action| + actions[\clear] = [nil,action]; + this.prSendMsg(this.clearMsg); + } - printMsg { ^this.prMakeMsg(\print,id); } + printMsg { ^this.prMakeMsg(\print,id); } print { |action=(postResponse)| actions[\print] = [string(FluidMessageResponse,_,_),action]; @@ -62,11 +62,11 @@ FluidLabelSet : FluidDataObject { } getIdsMsg{|labelSet| - ^this.prMakeMsg(\getIds, id, labelSet.asUGenInput); - } + ^this.prMakeMsg(\getIds, id, labelSet.asUGenInput); + } getIds{|labelSet, action| - actions[\getIds] = [nil,action]; - this.prSendMsg(this.getIdsMsg(labelSet)); + actions[\getIds] = [nil,action]; + this.prSendMsg(this.getIdsMsg(labelSet)); } } diff --git a/release-packaging/Classes/FluidLoudness.sc b/release-packaging/Classes/FluidLoudness.sc index 4018dc9..1fa8128 100644 --- a/release-packaging/Classes/FluidLoudness.sc +++ b/release-packaging/Classes/FluidLoudness.sc @@ -1,43 +1,43 @@ FluidLoudness : FluidRTMultiOutUGen { - const (1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; - }; - bits = a.collect{ |sym| - (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) - }.reduce{|x,y| x | y}; - ^bits - } + const (1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; + }; + bits = a.collect{ |sym| + (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) + }.reduce{|x,y| x | y}; + ^bits + } *kr { arg in = 0, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, maxWindowSize = 16384; - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; ^this.multiNew('control', in.asAudioRateInput(this), selectbits, kWeighting, truePeak, windowSize, hopSize, maxWindowSize); } - init {arg ...theInputs; - var numChannels; + init {arg ...theInputs; + var numChannels; inputs = theInputs; - numChannels = inputs.at(1).asBinaryDigits.sum; + numChannels = inputs.at(1).asBinaryDigits.sum; ^this.initOutputs(numChannels,rate); } checkInputs { if(inputs.at(6).rate != 'scalar') { ^(": maxwindowSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidMDS.sc b/release-packaging/Classes/FluidMDS.sc index 234f415..bba4133 100644 --- a/release-packaging/Classes/FluidMDS.sc +++ b/release-packaging/Classes/FluidMDS.sc @@ -7,25 +7,25 @@ FluidMDS : FluidModelObject { classvar < kl = 5; classvar < cosine = 5; - var <>numDimensions, <>distanceMetric; + var <>numDimensions, <>distanceMetric; *new {|server,numDimensions = 2, distanceMetric = 1| ^super.new(server,[numDimensions, distanceMetric]) - .numDimensions_(numDimensions) - .distanceMetric_(distanceMetric); + .numDimensions_(numDimensions) + .distanceMetric_(distanceMetric); } - prGetParams{ - ^[this.numDimensions, this.distanceMetric]; - } + prGetParams{ + ^[this.numDimensions, this.distanceMetric]; + } fitTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); - } + ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); + } fitTransform{|sourceDataSet, destDataSet, action| - actions[\fitTransform] = [nil,action]; - this.fitTransformMsg(sourceDataSet,destDataSet); + actions[\fitTransform] = [nil,action]; + this.fitTransformMsg(sourceDataSet,destDataSet); this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); } diff --git a/release-packaging/Classes/FluidMFCC.sc b/release-packaging/Classes/FluidMFCC.sc index e42cace..ccf2e36 100644 --- a/release-packaging/Classes/FluidMFCC.sc +++ b/release-packaging/Classes/FluidMFCC.sc @@ -2,8 +2,8 @@ FluidMFCC : FluidRTMultiOutUGen { *kr { arg in = 0, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumCoeffs = nil, maxNumBands = nil; - maxNumCoeffs = maxNumCoeffs ? numCoeffs; - maxNumBands = maxNumBands ? numBands; + maxNumCoeffs = maxNumCoeffs ? numCoeffs; + maxNumBands = maxNumBands ? numBands; ^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, maxNumCoeffs, numBands, maxNumBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize); } @@ -19,7 +19,7 @@ FluidMFCC : FluidRTMultiOutUGen { if(inputs.at(2).rate != 'scalar') { ^(": maxNumCoeffs cannot be modulated."); }; - if(inputs.at(4).rate != 'scalar') { + if(inputs.at(4).rate != 'scalar') { ^(": maxNumBands cannot be modulated."); }; if(inputs.at(10).rate != 'scalar') { diff --git a/release-packaging/Classes/FluidMLP.sc b/release-packaging/Classes/FluidMLP.sc index 7939f75..fc948e9 100644 --- a/release-packaging/Classes/FluidMLP.sc +++ b/release-packaging/Classes/FluidMLP.sc @@ -5,109 +5,109 @@ FluidMLPRegressor : FluidModelObject { const hiddenLayers, <>activation, <>outputActivation, <>tapIn, <>tapOut, <>maxIter, <>learnRate, <>momentum, <>batchSize, <>validation; + var <>hiddenLayers, <>activation, <>outputActivation, <>tapIn, <>tapOut, <>maxIter, <>learnRate, <>momentum, <>batchSize, <>validation; *new {|server, hiddenLayers = #[3,3] , activation = 2, outputActivation = 0, tapIn = 0, tapOut = -1,maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2| ^super.new(server, [hiddenLayers.size] ++ hiddenLayers ++ [activation, outputActivation, tapIn, tapOut, maxIter, learnRate, momentum, batchSize, validation]) - .hiddenLayers_(hiddenLayers) - .activation_(activation) - .outputActivation_(outputActivation) - .tapIn_(tapIn) - .tapOut_(tapOut) - .maxIter_(maxIter) - .learnRate_(learnRate) - .momentum_(momentum) - .batchSize_(batchSize) - .validation_(validation); - } - - prGetParams{ - ^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.outputActivation, this.tapIn, this.tapOut, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation] - } - - clearMsg{ ^this.prMakeMsg(\clear, id) } - - clear{ |action| - actions[\clear] = [nil, action]; + .hiddenLayers_(hiddenLayers) + .activation_(activation) + .outputActivation_(outputActivation) + .tapIn_(tapIn) + .tapOut_(tapOut) + .maxIter_(maxIter) + .learnRate_(learnRate) + .momentum_(momentum) + .batchSize_(batchSize) + .validation_(validation); + } + + prGetParams{ + ^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.outputActivation, this.tapIn, this.tapOut, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation] + } + + clearMsg{ ^this.prMakeMsg(\clear, id) } + + clear{ |action| + actions[\clear] = [nil, action]; this.prSendMsg(this.clearMsg); } - fitMsg{|sourceDataSet, targetDataSet| - ^this.prMakeMsg(\fit,id,sourceDataSet.id, targetDataSet.id); - } + fitMsg{|sourceDataSet, targetDataSet| + ^this.prMakeMsg(\fit,id,sourceDataSet.id, targetDataSet.id); + } fit{|sourceDataSet, targetDataSet, action| - actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action]; - this.prSendMsg(this.fitMsg(sourceDataSet,targetDataSet)); + actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.fitMsg(sourceDataSet,targetDataSet)); } - predictMsg{|sourceDataSet, targetDataSet| - ^this.prMakeMsg(\predict,id,sourceDataSet.id, targetDataSet.id); - } + predictMsg{|sourceDataSet, targetDataSet| + ^this.prMakeMsg(\predict,id,sourceDataSet.id, targetDataSet.id); + } predict{|sourceDataSet, targetDataSet, action| - actions[\predict] = [nil,action]; - this.prSendMsg(this.predictMsg(sourceDataSet,targetDataSet)); + actions[\predict] = [nil,action]; + this.prSendMsg(this.predictMsg(sourceDataSet,targetDataSet)); } predictPointMsg { |sourceBuffer, targetBuffer| - ^this.prMakeMsg(\predictPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(targetBuffer), - ["/b_query", targetBuffer.asUGenInput]); - } + ^this.prMakeMsg(\predictPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(targetBuffer), + ["/b_query", targetBuffer.asUGenInput]); + } predictPoint { |sourceBuffer, targetBuffer, action| - actions[\predictPoint] = [nil,{action.value(targetBuffer)}]; - this.predictPointMsg(sourceBuffer, targetBuffer); + actions[\predictPoint] = [nil,{action.value(targetBuffer)}]; + this.predictPointMsg(sourceBuffer, targetBuffer); this.prSendMsg(this.predictPointMsg(sourceBuffer, targetBuffer)); } - read { |filename, action| - actions[\read] = [numbers(FluidMessageResponse,_,nil,_), { - |data| - this.prUpdateParams(data); - action.value; - }]; - this.prSendMsg(this.readMsg(filename)); - } - - kr{|trig, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1| - var params; - tapIn = tapIn ? this.tapIn; - tapOut = tapOut ? this.tapOut; - - this.tapIn_(tapIn).tapOut_(tapOut); - - params = [this.prEncodeBuffer(inputBuffer), - this.prEncodeBuffer(outputBuffer),this.tapIn,this.tapOut]; - - ^FluidMLPRegressorQuery.kr(trig,this, *params); - } - - prUpdateParams{|data| - var rest = data.keep(-9); - this.hiddenLayers_(data.drop(1).drop(-9).copy); - [\activation_, \outputActivation_, - \tapIn_, \tapOut_, \maxIter_, - \learnRate_, \momentum_, - \batchSize_, \validation_] - .do{|prop,i| - this.performList(prop,rest[i]); - }; - } + read { |filename, action| + actions[\read] = [numbers(FluidMessageResponse,_,nil,_), { + |data| + this.prUpdateParams(data); + action.value; + }]; + this.prSendMsg(this.readMsg(filename)); + } + + kr{|trig, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1| + var params; + tapIn = tapIn ? this.tapIn; + tapOut = tapOut ? this.tapOut; + + this.tapIn_(tapIn).tapOut_(tapOut); + + params = [this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer),this.tapIn,this.tapOut]; + + ^FluidMLPRegressorQuery.kr(trig,this, *params); + } + + prUpdateParams{|data| + var rest = data.keep(-9); + this.hiddenLayers_(data.drop(1).drop(-9).copy); + [\activation_, \outputActivation_, + \tapIn_, \tapOut_, \maxIter_, + \learnRate_, \momentum_, + \batchSize_, \validation_] + .do{|prop,i| + this.performList(prop,rest[i]); + }; + } } FluidMLPRegressorQuery : FluidRTMultiOutUGen { - *kr{ |trig, model, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1| - ^this.multiNew('control',trig, model.asUGenInput, - tapIn, tapOut, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1| + ^this.multiNew('control',trig, model.asUGenInput, + tapIn, tapOut, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } @@ -120,94 +120,94 @@ FluidMLPClassifier : FluidModelObject { const hiddenLayers, <>activation, <> maxIter, <>learnRate, <> momentum, <>batchSize, <>validation; + var <>hiddenLayers, <>activation, <> maxIter, <>learnRate, <> momentum, <>batchSize, <>validation; *new {|server, hiddenLayers = #[3,3] , activation = 2, maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2| ^super.new(server,[hiddenLayers.size] ++ hiddenLayers ++ [activation, maxIter, learnRate, momentum, batchSize, validation]) - .hiddenLayers_(hiddenLayers) - .activation_(activation) - .maxIter_(maxIter) - .learnRate_(learnRate) - .momentum_(momentum) - .batchSize_(batchSize) - .validation_(validation); + .hiddenLayers_(hiddenLayers) + .activation_(activation) + .maxIter_(maxIter) + .learnRate_(learnRate) + .momentum_(momentum) + .batchSize_(batchSize) + .validation_(validation); } - prGetParams{ - ^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation]; - } + prGetParams{ + ^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation]; + } - clearMsg{ ^this.prMakeMsg(\clear,id) } + clearMsg{ ^this.prMakeMsg(\clear,id) } clear{ |action| - actions[\clear] = [nil,action]; + actions[\clear] = [nil,action]; this.prSendMsg(this.clearMsg); } - fitMsg{|sourceDataSet, targetLabelSet| - ^this.prMakeMsg(\fit,id,sourceDataSet.id, targetLabelSet.id); - } + fitMsg{|sourceDataSet, targetLabelSet| + ^this.prMakeMsg(\fit,id,sourceDataSet.id, targetLabelSet.id); + } fit{|sourceDataSet, targetLabelSet, action| - actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action]; - this.prSendMsg(this.fitMsg(sourceDataSet,targetLabelSet)); + actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.fitMsg(sourceDataSet,targetLabelSet)); } - predictMsg{|sourceDataSet, targetLabelSet| - ^this.prMakeMsg(\predict,id,sourceDataSet.id, targetLabelSet.id); - } + predictMsg{|sourceDataSet, targetLabelSet| + ^this.prMakeMsg(\predict,id,sourceDataSet.id, targetLabelSet.id); + } predict{ |sourceDataSet, targetLabelSet, action| - actions[\predict]=[nil,action]; + actions[\predict]=[nil,action]; this.prSendMsg(this.predictMsg(sourceDataSet,targetLabelSet)); } - predictPointMsg { |sourceBuffer| - ^this.prMakeMsg(\predictPoint,id,this.prEncodeBuffer(sourceBuffer)) - } + predictPointMsg { |sourceBuffer| + ^this.prMakeMsg(\predictPoint,id,this.prEncodeBuffer(sourceBuffer)) + } predictPoint { |sourceBuffer, action| - actions[\predictPoint] = [string(FluidMessageResponse,_,_),action]; + actions[\predictPoint] = [string(FluidMessageResponse,_,_),action]; this.prSendMsg(this.predictPointMsg(sourceBuffer)); } - read { |filename, action| - actions[\read] = [numbers(FluidMessageResponse,_,nil,_), { - |data| - this.prUpdateParams(data); - action.value; - }]; - this.prSendMsg(this.readMsg(filename)); - } + read { |filename, action| + actions[\read] = [numbers(FluidMessageResponse,_,nil,_), { + |data| + this.prUpdateParams(data); + action.value; + }]; + this.prSendMsg(this.readMsg(filename)); + } - prUpdateParams{|data| - var rest = data.keep(-6); - this.hiddenLayers_(data.drop(1).drop(-6).copy); - [\activation_, \maxIter_, - \learnRate_, \momentum_, - \batchSize_, \validation_] - .do{|prop,i| - this.performList(prop,rest[i]); - }; - } + prUpdateParams{|data| + var rest = data.keep(-6); + this.hiddenLayers_(data.drop(1).drop(-6).copy); + [\activation_, \maxIter_, + \learnRate_, \momentum_, + \batchSize_, \validation_] + .do{|prop,i| + this.performList(prop,rest[i]); + }; + } - kr{|trig, inputBuffer,outputBuffer| + kr{|trig, inputBuffer,outputBuffer| - var params = [this.prEncodeBuffer(inputBuffer), - this.prEncodeBuffer(outputBuffer)]; + var params = [this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)]; - ^FluidMLPClassifierQuery.kr(trig,this, *params); - } + ^FluidMLPClassifierQuery.kr(trig,this, *params); + } } FluidMLPClassifierQuery : FluidRTMultiOutUGen { - *kr{ |trig, model, inputBuffer,outputBuffer| - ^this.multiNew('control',trig, model.asUGenInput, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model, inputBuffer,outputBuffer| + ^this.multiNew('control',trig, model.asUGenInput, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidManipulationJSON.sc b/release-packaging/Classes/FluidManipulationJSON.sc index 875902b..3494299 100644 --- a/release-packaging/Classes/FluidManipulationJSON.sc +++ b/release-packaging/Classes/FluidManipulationJSON.sc @@ -8,17 +8,17 @@ var filename = this.tmpJSONFilename; action ?? {action = postResponse}; this.write(filename, { - action.value(this.parseJSON(File.readAllString(filename))); - File.delete(filename); - }); + action.value(this.parseJSON(File.readAllString(filename))); + File.delete(filename); + }); } load{|dict, action| var filename = this.tmpJSONFilename; File.use(filename, "wt", { |f| f.write(this.asJSON(dict));}); this.read(filename, { - action.notNil.if{ action.value; }; - File.delete(filename); + action.notNil.if{ action.value; }; + File.delete(filename); }); } @@ -55,9 +55,9 @@ if(d.isKindOf(Symbol)){^this.asJSON(d.asString)}; if(d.isKindOf(Dictionary)) { - ^"{" ++ ( - d.keys.asList.collect{|k| - k.asString.asCompileString ++ ":" + this.asJSON(d[k]) + ^"{" ++ ( + d.keys.asList.collect{|k| + k.asString.asCompileString ++ ":" + this.asJSON(d[k]) }).join(", ") ++ "}" }; if(d.isKindOf(SequenceableCollection)) diff --git a/release-packaging/Classes/FluidMelBands.sc b/release-packaging/Classes/FluidMelBands.sc index 486477f..1cbce09 100644 --- a/release-packaging/Classes/FluidMelBands.sc +++ b/release-packaging/Classes/FluidMelBands.sc @@ -1,9 +1,9 @@ FluidMelBands : FluidRTMultiOutUGen { *kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumBands; - - maxNumBands = maxNumBands ? numBands; - + + maxNumBands = maxNumBands ? numBands; + ^this.multiNew('control', in.asAudioRateInput(this), numBands, maxNumBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize); } diff --git a/release-packaging/Classes/FluidMessageResponse.sc b/release-packaging/Classes/FluidMessageResponse.sc index 0ebba79..ab34920 100644 --- a/release-packaging/Classes/FluidMessageResponse.sc +++ b/release-packaging/Classes/FluidMessageResponse.sc @@ -1,41 +1,41 @@ FluidMessageResponse : Object { - //selectors is an array of functions - //my cunning thought is that those that need extra data (e..g numbers()) can - //use partial applicaiton - *collectArgs{ |selectors,a| - var response = []; - var idx = 0; - selectors.do{ |selector| - var newThings; - # newThings,idx = selector.value(a, idx); - response = response ++ newThings; - }; + //selectors is an array of functions + //my cunning thought is that those that need extra data (e..g numbers()) can + //use partial applicaiton + *collectArgs{ |selectors,a| + var response = []; + var idx = 0; + selectors.do{ |selector| + var newThings; + # newThings,idx = selector.value(a, idx); + response = response ++ newThings; + }; - if(response.size == 1, - {^response[0]},{^response}) - } + if(response.size == 1, + {^response[0]},{^response}) + } - *string{ |a, offset| - ^[a] - } + *string{ |a, offset| + ^[a] + } - *strings {|a,offset| - //TODO add an n argument as with numbers() to make this less omnivorous - ^[a.drop(offset)]; - } + *strings {|a,offset| + //TODO add an n argument as with numbers() to make this less omnivorous + ^[a.drop(offset)]; + } - *numbers{ |a, n, offset| - n = n ? a.size - offset; //send n = nil to consume everything - ^[a.copyRange(offset, offset + n),offset + n] - } + *numbers{ |a, n, offset| + n = n ? a.size - offset; //send n = nil to consume everything + ^[a.copyRange(offset, offset + n),offset + n] + } - *number{ |a,offset| - ^[a[offset]]; - } + *number{ |a,offset| + ^[a[offset]]; + } - *buffer{ |a,server,offset| - server = server ? Server.default ; - ^[Buffer.cachedBufferAt(server, a[offset]), offset + 1] - } + *buffer{ |a,server,offset| + server = server ? Server.default ; + ^[Buffer.cachedBufferAt(server, a[offset]), offset + 1] + } } diff --git a/release-packaging/Classes/FluidNMFFilter.sc b/release-packaging/Classes/FluidNMFFilter.sc index 6c60c49..5b09dae 100644 --- a/release-packaging/Classes/FluidNMFFilter.sc +++ b/release-packaging/Classes/FluidNMFFilter.sc @@ -10,11 +10,11 @@ FluidNMFFilter : FluidRTMultiOutUGen { } checkInputs { - if(inputs.at(3).rate != 'scalar') { - ^(": maxComponents cannot be modulated."); + if(inputs.at(3).rate != 'scalar') { + ^(": maxComponents cannot be modulated."); }; - if(inputs.at(8).rate != 'scalar') { - ^(": maxFFTSize cannot be modulated."); + if(inputs.at(8).rate != 'scalar') { + ^(": maxFFTSize cannot be modulated."); }; ^this.checkValidInputs; } diff --git a/release-packaging/Classes/FluidNMFMatch.sc b/release-packaging/Classes/FluidNMFMatch.sc index a0904a6..e7009fc 100644 --- a/release-packaging/Classes/FluidNMFMatch.sc +++ b/release-packaging/Classes/FluidNMFMatch.sc @@ -10,11 +10,11 @@ FluidNMFMatch : FluidRTMultiOutUGen { } checkInputs { - if(inputs.at(3).rate != 'scalar') { - ^(": maxComponents cannot be modulated."); + if(inputs.at(3).rate != 'scalar') { + ^(": maxComponents cannot be modulated."); }; - if(inputs.at(8).rate != 'scalar') { - ^(": maxFFTSize cannot be modulated."); + if(inputs.at(8).rate != 'scalar') { + ^(": maxFFTSize cannot be modulated."); }; ^this.checkValidInputs; } diff --git a/release-packaging/Classes/FluidNRTProcess.sc b/release-packaging/Classes/FluidNRTProcess.sc index c8df9ea..ad53137 100644 --- a/release-packaging/Classes/FluidNRTProcess.sc +++ b/release-packaging/Classes/FluidNRTProcess.sc @@ -1,5 +1,5 @@ FluidNRTProcess : Object{ - var min, <>max; + var <>min, <>max; *new {|server, min = 0, max = 1| ^super.new(server,[min,max]) - .min_(min).max_(max); + .min_(min).max_(max); } - prGetParams{ - ^[this.id, this.min,this.max,-1,-1]; - } + prGetParams{ + ^[this.id, this.min,this.max,-1,-1]; + } - fitMsg{|dataSet| - ^this.prMakeMsg(\fit,id,dataSet.id) - } + fitMsg{|dataSet| + ^this.prMakeMsg(\fit,id,dataSet.id) + } fit{|dataSet, action| - actions[\fit] = [nil,action]; + actions[\fit] = [nil,action]; this.prSendMsg(this.fitMsg(dataSet)); } - transformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); - } + transformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); + } transform{|sourceDataSet, destDataSet, action| actions[\transform] = [nil,action]; - this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet)); + this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet)); } - fitTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) - } + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) + } fitTransform{|sourceDataSet, destDataSet, action| - actions[\fitTransform] = [nil,action]; + actions[\fitTransform] = [nil,action]; this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet)); } - transformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\transformPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(destBuffer), - ["/b_query",destBuffer.asUGenInput] - ); - } + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } transformPoint{|sourceBuffer, destBuffer, action| - actions[\transformPoint] = [nil,{action.value(destBuffer)}]; - this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer)); + actions[\transformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer)); } - inverseTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id); - } + inverseTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id); + } inverseTransform{|sourceDataSet, destDataSet, action| actions[\inverseTransform] = [nil,action]; - this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); + this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); } - inverseTransformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\inverseTransformPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(destBuffer), - ["/b_query",destBuffer.asUGenInput] - ); - } + inverseTransformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\inverseTransformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } inverseTransformPoint{|sourceBuffer, destBuffer, action| - actions[\inverseTransformPoint] = [nil,{action.value(destBuffer)}]; - this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer)); + actions[\inverseTransformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer)); } - kr{|trig, inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0| + kr{|trig, inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0| - min = min ? this.min; - max = max ? this.max; + min = min ? this.min; + max = max ? this.max; - this.min_(min).max_(max); + this.min_(min).max_(max); - ^FluidNormalizeQuery.kr(trig, - this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.min, this.max, invert); - } + ^FluidNormalizeQuery.kr(trig, + this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.min, this.max, invert); + } } FluidNormalizeQuery : FluidRTMultiOutUGen { - *kr{ |trig, model,inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0| + *kr{ |trig, model,inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0| // inputBuffer.asUGenInput.postln; - ^this.multiNew('control',trig, model.asUGenInput, - min,max,invert, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + ^this.multiNew('control',trig, model.asUGenInput, + min,max,invert, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidNoveltyFeature.sc b/release-packaging/Classes/FluidNoveltyFeature.sc index a83df30..a663bef 100644 --- a/release-packaging/Classes/FluidNoveltyFeature.sc +++ b/release-packaging/Classes/FluidNoveltyFeature.sc @@ -1,11 +1,11 @@ FluidNoveltyFeature : FluidRTUGen { *kr { arg in = 0, algorithm = 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxKernelSize, maxFilterSize; - maxKernelSize = maxKernelSize ? kernelSize; - maxFilterSize = maxFilterSize ? filterSize; - algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm) ?? { - ("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm); - }; + maxKernelSize = maxKernelSize ? kernelSize; + maxFilterSize = maxFilterSize ? filterSize; + algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm) ?? { + ("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm); + }; ^this.multiNew('control', in.asAudioRateInput(this), algorithm, kernelSize, maxKernelSize, filterSize, maxFilterSize, windowSize, hopSize, fftSize, maxFFTSize) } @@ -13,16 +13,16 @@ FluidNoveltyFeature : FluidRTUGen { checkInputs { if([\scalar, \control].includes(inputs.at(1).rate).not) { ^(": invalid algorithm"); - }; + }; if(inputs.at(9).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); - }; + }; if(inputs.at(3).rate != 'scalar') { ^(": maxKernelSize cannot be modulated."); - }; + }; if(inputs.at(5).rate != 'scalar') { ^(": maxFilterSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidNoveltySlice.sc b/release-packaging/Classes/FluidNoveltySlice.sc index da43d54..12014b6 100644 --- a/release-packaging/Classes/FluidNoveltySlice.sc +++ b/release-packaging/Classes/FluidNoveltySlice.sc @@ -16,12 +16,12 @@ FluidNoveltySlice : FluidRTUGen { *ar { arg in = 0, algorithm = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxKernelSize, maxFilterSize; - maxKernelSize = maxKernelSize ? kernelSize; - maxFilterSize = maxFilterSize ? filterSize; + maxKernelSize = maxKernelSize ? kernelSize; + maxFilterSize = maxFilterSize ? filterSize; - algorithm = this.prSelectAlgorithm(algorithm) ?? { - ("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm); - }; + algorithm = this.prSelectAlgorithm(algorithm) ?? { + ("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm); + }; ^this.multiNew('audio', in.asAudioRateInput(this), algorithm, kernelSize, maxKernelSize, threshold, filterSize, maxFilterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize) } @@ -29,16 +29,16 @@ FluidNoveltySlice : FluidRTUGen { checkInputs { if([\scalar, \control].includes(inputs.at(1).rate).not) { ^(": invalid algorithm"); - }; + }; if(inputs.at(11).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); - }; + }; if(inputs.at(3).rate != 'scalar') { ^(": maxKernelSize cannot be modulated."); - }; + }; if(inputs.at(6).rate != 'scalar') { ^(": maxFilterSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidOnsetFeature.sc b/release-packaging/Classes/FluidOnsetFeature.sc index d5b7786..35416ed 100644 --- a/release-packaging/Classes/FluidOnsetFeature.sc +++ b/release-packaging/Classes/FluidOnsetFeature.sc @@ -10,10 +10,10 @@ FluidOnsetFeature : FluidRTUGen { checkInputs { if([\scalar, \control].includes(inputs.at(1).rate).not) { ^(": invalid metric"); - }; + }; if(inputs.at(7).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidOnsetSlice.sc b/release-packaging/Classes/FluidOnsetSlice.sc index 507e128..947f659 100644 --- a/release-packaging/Classes/FluidOnsetSlice.sc +++ b/release-packaging/Classes/FluidOnsetSlice.sc @@ -36,10 +36,10 @@ FluidOnsetSlice : FluidRTUGen { checkInputs { if([\scalar, \control].includes(inputs.at(1).rate).not) { ^(": invalid metric"); - }; + }; if(inputs.at(9).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidPCA.sc b/release-packaging/Classes/FluidPCA.sc index eb79d79..03bdf77 100644 --- a/release-packaging/Classes/FluidPCA.sc +++ b/release-packaging/Classes/FluidPCA.sc @@ -1,62 +1,62 @@ FluidPCA : FluidModelObject{ - var <>numDimensions, <>whiten; + var <>numDimensions, <>whiten; - *new {|server, numDimensions = 2, whiten = 0| + *new {|server, numDimensions = 2, whiten = 0| ^super.new(server,[numDimensions, whiten]).numDimensions_(numDimensions).whiten_(whiten); - } + } - prGetParams{ - ^[this.id, numDimensions, whiten]; - } + prGetParams{ + ^[this.id, numDimensions, whiten]; + } - fitMsg{|dataSet| - ^this.prMakeMsg(\fit,id, dataSet.id); - } + fitMsg{|dataSet| + ^this.prMakeMsg(\fit,id, dataSet.id); + } - fit{|dataSet, action| - actions[\fit] = [nil, action]; - this.prSendMsg(this.fitMsg(dataSet)); - } + fit{|dataSet, action| + actions[\fit] = [nil, action]; + this.prSendMsg(this.fitMsg(dataSet)); + } - transformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id); - } + transformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id); + } - transform{|sourceDataSet, destDataSet, action| - actions[\transform] = [numbers(FluidMessageResponse,_,1,_),action]; - this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet)); - } + transform{|sourceDataSet, destDataSet, action| + actions[\transform] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet)); + } - fitTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); - } + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); + } - fitTransform{|sourceDataSet, destDataSet, action| - actions[\fitTransform] = [numbers(FluidMessageResponse,_,1,_),action]; - this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); - } + fitTransform{|sourceDataSet, destDataSet, action| + actions[\fitTransform] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); + } - transformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\transformPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(destBuffer), - ["/b_query",destBuffer.asUGenInput] - ); - } + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } - transformPoint{|sourceBuffer, destBuffer, action| - actions[\transformPoint] = [nil,{action.value(destBuffer)}]; - this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); - } + transformPoint{|sourceBuffer, destBuffer, action| + actions[\transformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); + } - kr{|trig, inputBuffer,outputBuffer,numDimensions| + kr{|trig, inputBuffer,outputBuffer,numDimensions| - numDimensions = numDimensions ? this.numDimensions; - this.numDimensions_(numDimensions); + numDimensions = numDimensions ? this.numDimensions; + this.numDimensions_(numDimensions); - ^FluidPCAQuery.kr(trig ,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.numDimensions, this.whiten); - } + ^FluidPCAQuery.kr(trig ,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.numDimensions, this.whiten); + } inverseTransformPointMsg{|sourceBuffer, destBuffer| ^this.prMakeMsg(\inverseTransformPoint,id, @@ -71,26 +71,26 @@ FluidPCA : FluidModelObject{ this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer,destBuffer)); } - inverseTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id, destDataSet.id); - } + inverseTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id, destDataSet.id); + } - inverseTransform{|sourceDataSet, destDataSet,action| - actions[\inverseTransform] = [nil,action]; - this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); - } + inverseTransform{|sourceDataSet, destDataSet,action| + actions[\inverseTransform] = [nil,action]; + this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); + } } FluidPCAQuery : FluidRTMultiOutUGen { - *kr{ |trig, model, inputBuffer,outputBuffer,numDimensions, whiten| - ^this.multiNew('control',trig, model.asUGenInput, - numDimensions, whiten, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model, inputBuffer,outputBuffer,numDimensions, whiten| + ^this.multiNew('control',trig, model.asUGenInput, + numDimensions, whiten, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidPitch.sc b/release-packaging/Classes/FluidPitch.sc index da15c3f..079e90f 100644 --- a/release-packaging/Classes/FluidPitch.sc +++ b/release-packaging/Classes/FluidPitch.sc @@ -1,44 +1,44 @@ FluidPitch : FluidRTMultiOutUGen { - const (1<(1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; - }; - bits = a.collect{ |sym| - (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) - }.reduce{|x,y| x | y}; - ^bits - } + *prWarnUnrecognised {|sym| ("WARNING: FluidPitch -" + sym + "is not a recognised option").postln} + + *prProcessSelect {|a| + var bits; + a.asBag.countsDo{|item,count,i| + if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn}; + }; + bits = a.collect{ |sym| + (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) + }.reduce{|x,y| x | y}; + ^bits + } *kr { arg in = 0, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1; - - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - + + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + ^this.multiNew('control', in.asAudioRateInput(this), selectbits, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; - var numChannels; + var numChannels; inputs = theInputs; - numChannels = inputs.at(1).asBinaryDigits.sum; + numChannels = inputs.at(1).asBinaryDigits.sum; ^this.initOutputs(numChannels,rate); } checkInputs { if(inputs.at(9).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidRobustScale.sc b/release-packaging/Classes/FluidRobustScale.sc index 87ebefa..d884146 100644 --- a/release-packaging/Classes/FluidRobustScale.sc +++ b/release-packaging/Classes/FluidRobustScale.sc @@ -1,95 +1,95 @@ FluidRobustScale : FluidModelObject { - var <>low, <>high; + var <>low, <>high; *new {|server, low = 25, high = 75| ^super.new(server,[low,high]) .low_(low).high_(high); } - prGetParams{ - ^[this.id,this.low,this.high]; - } + prGetParams{ + ^[this.id,this.low,this.high]; + } - fitMsg{|dataSet| - ^this.prMakeMsg(\fit,id,dataSet.id) - } + fitMsg{|dataSet| + ^this.prMakeMsg(\fit,id,dataSet.id) + } fit{|dataSet, action| - actions[\fit] = [nil,action]; + actions[\fit] = [nil,action]; this.prSendMsg(this.fitMsg(dataSet)); } - transformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); - } + transformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); + } transform{|sourceDataSet, destDataSet, action| actions[\transform] = [nil,action]; - this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet)); + this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet)); } - fitTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) - } + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) + } fitTransform{|sourceDataSet, destDataSet, action| - actions[\fitTransform] = [nil,action]; + actions[\fitTransform] = [nil,action]; this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet)); } - transformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\transformPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(destBuffer), - ["/b_query",destBuffer.asUGenInput] - ); - } + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } transformPoint{|sourceBuffer, destBuffer, action| - actions[\transformPoint] = [nil,{action.value(destBuffer)}]; - this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer)); + actions[\transformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer)); } - inverseTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id); - } + inverseTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id); + } inverseTransform{|sourceDataSet, destDataSet, action| actions[\inverseTransform] = [nil,action]; - this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); + this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); } - inverseTransformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\inverseTransformPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(destBuffer), - ["/b_query",destBuffer.asUGenInput] - ); - } + inverseTransformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\inverseTransformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } inverseTransformPoint{|sourceBuffer, destBuffer, action| - actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}]; - this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer)); + actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer)); } - kr{|trig, inputBuffer,outputBuffer,invert = 0| + kr{|trig, inputBuffer,outputBuffer,invert = 0| - ^FluidRobustScaleQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert); - } + ^FluidRobustScaleQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert); + } } FluidRobustScaleQuery : FluidRTMultiOutUGen { - *kr{ |trig, model, inputBuffer,outputBuffer,invert| - ^this.multiNew('control',trig, model.asUGenInput, - invert, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model, inputBuffer,outputBuffer,invert| + ^this.multiNew('control',trig, model.asUGenInput, + invert, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidSKMeans.sc b/release-packaging/Classes/FluidSKMeans.sc index f53d450..48e8de0 100644 --- a/release-packaging/Classes/FluidSKMeans.sc +++ b/release-packaging/Classes/FluidSKMeans.sc @@ -85,9 +85,9 @@ FluidSKMeans : FluidModelObject { encodePointMsg{ |sourceBuffer, targetBuffer| ^this.prMakeMsg(\encodePoint, id, this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(targetBuffer), - ["/b_query", targetBuffer.asUGenInput]); - } + this.prEncodeBuffer(targetBuffer), + ["/b_query", targetBuffer.asUGenInput]); + } encodePoint { |sourceBuffer, targetBuffer, action| actions[\encodePoint] = [nil,{action.value(targetBuffer)}]; @@ -96,40 +96,40 @@ FluidSKMeans : FluidModelObject { getMeansMsg{|dataSet| ^this.prMakeMsg(\getMeans, id, dataSet.asUGenInput) } - getMeans{ |dataSet, action| - actions[\getMeans] = [nil, action]; + getMeans{ |dataSet, action| + actions[\getMeans] = [nil, action]; this.prSendMsg(this.getMeansMsg(dataSet)); } setMeansMsg{|dataSet| ^this.prMakeMsg(\setMeans, id, dataSet.asUGenInput) } - setMeans{ |dataSet, action| - actions[\setMeans] = [nil, action]; + setMeans{ |dataSet, action| + actions[\setMeans] = [nil, action]; this.prSendMsg(this.setMeansMsg(dataSet)); } clearMsg{ ^this.prMakeMsg(\clear, id) } - clear{ |action| - actions[\clear] = [nil, action]; + clear{ |action| + actions[\clear] = [nil, action]; this.prSendMsg(this.clearMsg); } kr{|trig, inputBuffer,outputBuffer| - ^FluidSKMeansQuery.kr(trig, - this, - this.prEncodeBuffer(inputBuffer), - this.prEncodeBuffer(outputBuffer)); + ^FluidSKMeansQuery.kr(trig, + this, + this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)); } } FluidSKMeansQuery : FluidRTMultiOutUGen { - *kr{ |trig, model,inputBuffer, outputBuffer | - ^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model,inputBuffer, outputBuffer | + ^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidSTFTPass.sc b/release-packaging/Classes/FluidSTFTPass.sc index da49806..ec56a41 100644 --- a/release-packaging/Classes/FluidSTFTPass.sc +++ b/release-packaging/Classes/FluidSTFTPass.sc @@ -5,7 +5,7 @@ FluidSTFTPass : FluidRTUGen { checkInputs { if(inputs.at(4).rate != 'scalar') { ^": maxFFTSize cannot be modulated."; - }; + }; ^this.checkValidInputs } } diff --git a/release-packaging/Classes/FluidServerCache.sc b/release-packaging/Classes/FluidServerCache.sc index 6f9a3c9..6e97fcd 100644 --- a/release-packaging/Classes/FluidServerCache.sc +++ b/release-packaging/Classes/FluidServerCache.sc @@ -3,7 +3,7 @@ FluidProxyUgen : UGen { var <>pluginname; *kr { |pluginname...args| - args = args.collect{|x| x.asUGenInput} + args = args.collect{|x| x.asUGenInput} ^this.new1('control', pluginname,*args) } @@ -17,9 +17,9 @@ FluidProxyUgen : UGen { ^pluginname.asString; } - poll{ |trig = 10, label, trigid = -1| - ^super.poll(trig, label ? this.name, trigid) - } + poll{ |trig = 10, label, trigid = -1| + ^super.poll(trig, label ? this.name, trigid) + } } FluidServerCache { @@ -36,17 +36,17 @@ FluidServerCache { cache[server]!?{cache[server].do{|x|func.value(x)}} } - doAll {|func| - cache.do{|subCache| - subCache.do{|item| - func.value(item) - } - } - } + doAll {|func| + cache.do{|subCache| + subCache.do{|item| + func.value(item) + } + } + } - postln{ - cache.postln; - } + postln{ + cache.postln; + } at { |server,id| ^cache[server].tryPerform(\at,id) @@ -68,7 +68,7 @@ FluidServerCache { cache[server] ?? { cache[server] = IdentityDictionary.new; NotificationCenter.register(server,\newAllocators,this, - { + { this.clearCache(server); }); } @@ -76,18 +76,18 @@ FluidServerCache { clearCache { |server| cache[server] !? - { - var bundle = []; - cache[server].values.do{|i| - if(i.respondsTo(\freeMsg)){ - bundle = bundle.add(i.freeMsg); //server objects - }{ - i.free; //OSCFunc - } - }; - server.listSendBundle(nil,bundle); - cache.removeAt(server); - }; + { + var bundle = []; + cache[server].values.do{|i| + if(i.respondsTo(\freeMsg)){ + bundle = bundle.add(i.freeMsg); //server objects + }{ + i.free; //OSCFunc + } + }; + server.listSendBundle(nil,bundle); + cache.removeAt(server); + }; } } diff --git a/release-packaging/Classes/FluidServerObject.sc b/release-packaging/Classes/FluidServerObject.sc index 2fa5970..d724012 100644 --- a/release-packaging/Classes/FluidServerObject.sc +++ b/release-packaging/Classes/FluidServerObject.sc @@ -1,346 +1,346 @@ FluidServerObject { - classvar serverCaches; - classvar count; - classvar persistent = true; - var (1<(1< 1) { ("Option '" ++ item ++ "' is repeated").warn}; - }; - bits = a.collect{ |sym| - (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) - }.reduce{|x,y| x | y}; - ^bits - } + *prWarnUnrecognised {|sym| ("WARNING: FluidSpectralShape -" + sym + "is not a recognised option").postln} + + *prProcessSelect {|a| + var bits; + a.asBag.countsDo{|item,count,i| + if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn}; + }; + bits = a.collect{ |sym| + (featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0}) + }.reduce{|x,y| x | y}; + ^bits + } - *kr { arg in = 0, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1; + *kr { arg in = 0, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1; - var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; + var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)}; - ^this.multiNew('control', in.asAudioRateInput(this), selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize); + ^this.multiNew('control', in.asAudioRateInput(this), selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; - var numChannels; + var numChannels; inputs = theInputs; - numChannels = inputs.at(1).asBinaryDigits.sum; + numChannels = inputs.at(1).asBinaryDigits.sum; ^this.initOutputs(numChannels,rate); } checkInputs { if(inputs.at(10).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); - }; + }; ^this.checkValidInputs; } } diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index 05f0f4a..527e5b8 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -1,86 +1,86 @@ FluidStandardize : FluidModelObject { - *new {|server| + *new {|server| ^super.new(server,[]); } - prGetParams{ - ^[this.id]; - } + prGetParams{ + ^[this.id]; + } fitMsg{|dataSet| - ^this.prMakeMsg(\fit,id,dataSet.id); - } + ^this.prMakeMsg(\fit,id,dataSet.id); + } - fit{|dataSet, action| - actions[\fit] = [nil, action]; + fit{|dataSet, action| + actions[\fit] = [nil, action]; this.prSendMsg(this.fitMsg(dataSet)); } transformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); - } + ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); + } transform{|sourceDataSet, destDataSet, action| - actions[\transform] = [nil,action]; + actions[\transform] = [nil,action]; this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet)); } - fitTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) - } + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) + } fitTransform{|sourceDataSet, destDataSet, action| - actions[\fitTransform] = [nil,action]; + actions[\fitTransform] = [nil,action]; this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet)); } - transformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\transformPoint, id, this.prEncodeBuffer(sourceBuffer), this.prEncodeBuffer(destBuffer),["/b_query",destBuffer.asUGenInput]); - } + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint, id, this.prEncodeBuffer(sourceBuffer), this.prEncodeBuffer(destBuffer),["/b_query",destBuffer.asUGenInput]); + } transformPoint{|sourceBuffer, destBuffer, action| - actions[\transformPoint] = [nil, {action.value(destBuffer)}]; - this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); + actions[\transformPoint] = [nil, {action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); } - inverseTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id); - } + inverseTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id); + } inverseTransform{|sourceDataSet, destDataSet, action| actions[\inverseTransform] = [nil,action]; - this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); + this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet)); } - inverseTransformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\inverseTransformPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(destBuffer), - ["/b_query",destBuffer.asUGenInput] - ); - } + inverseTransformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\inverseTransformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } inverseTransformPoint{|sourceBuffer, destBuffer, action| - actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}]; - this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer)); + actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer)); } - kr{|trig, inputBuffer,outputBuffer,invert = 0| + kr{|trig, inputBuffer,outputBuffer,invert = 0| - ^FluidStandardizeQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert); - } + ^FluidStandardizeQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert); + } } FluidStandardizeQuery : FluidRTMultiOutUGen { - *kr{ |trig, model,inputBuffer,outputBuffer,invert = 0| - ^this.multiNew('control',trig, model.asUGenInput, - invert, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model,inputBuffer,outputBuffer,invert = 0| + ^this.multiNew('control',trig, model.asUGenInput, + invert, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidStats.sc b/release-packaging/Classes/FluidStats.sc index bb286de..c22b873 100644 --- a/release-packaging/Classes/FluidStats.sc +++ b/release-packaging/Classes/FluidStats.sc @@ -1,12 +1,12 @@ FluidStats : MultiOutUGen { *kr { arg in, history; - ^this.multiNew('control',*(in.asArray++history)).reshape(2,in.asArray.size); + ^this.multiNew('control',*(in.asArray++history)).reshape(2,in.asArray.size); } init {arg ...theInputs; inputs = theInputs; - this.specialIndex = (inputs.size - 2).max(0); + this.specialIndex = (inputs.size - 2).max(0); ^this.initOutputs(inputs.size - 1,rate) } @@ -14,17 +14,17 @@ FluidStats : MultiOutUGen { ^this.checkValidInputs; } - initOutputs{|numChans,rate| - if(numChans.isNil or: {numChans < 1}) - { - Error("No input channels").throw - }; + initOutputs{|numChans,rate| + if(numChans.isNil or: {numChans < 1}) + { + Error("No input channels").throw + }; - channels = Array.fill(numChans * 2, { |i| - OutputProxy('control',this,i); - }); - ^channels - } + channels = Array.fill(numChans * 2, { |i| + OutputProxy('control',this,i); + }); + ^channels + } - numOutputs { ^(channels.size); } + numOutputs { ^(channels.size); } } diff --git a/release-packaging/Classes/FluidUMAP.sc b/release-packaging/Classes/FluidUMAP.sc index 24f632c..cce35cd 100644 --- a/release-packaging/Classes/FluidUMAP.sc +++ b/release-packaging/Classes/FluidUMAP.sc @@ -1,75 +1,75 @@ FluidUMAP : FluidModelObject { - var <>numDimensions, <>numNeighbours, <>minDist, <>iterations, <>learnRate; + var <>numDimensions, <>numNeighbours, <>minDist, <>iterations, <>learnRate; *new {|server,numDimensions = 2, numNeighbours = 15, minDist = 0.1, iterations = 200, learnRate = 0.1| ^super.new(server,[numDimensions, numNeighbours, minDist, iterations, learnRate]) - .numDimensions_(numDimensions) - .numNeighbours_(numNeighbours) - .minDist_(minDist) - .iterations_(iterations) - .learnRate_(learnRate); + .numDimensions_(numDimensions) + .numNeighbours_(numNeighbours) + .minDist_(minDist) + .iterations_(iterations) + .learnRate_(learnRate); } - prGetParams{ - ^[ - this.id, - this.numDimensions, - this.numNeighbours, - this.minDist, - this.iterations, - this.learnRate, - ] - } - - fitTransformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\fitTransform, id, sourceDataSet.id, destDataSet.id) - } + prGetParams{ + ^[ + this.id, + this.numDimensions, + this.numNeighbours, + this.minDist, + this.iterations, + this.learnRate, + ] + } + + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform, id, sourceDataSet.id, destDataSet.id) + } fitTransform{|sourceDataSet, destDataSet, action| - actions[\fitTransform] = [nil, action]; + actions[\fitTransform] = [nil, action]; this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); } fitMsg{|dataSet| - ^this.prMakeMsg(\fit,id, dataSet.id); - } + ^this.prMakeMsg(\fit,id, dataSet.id); + } - fit{|dataSet, action| - actions[\fit] = [nil, action]; - this.prSendMsg(this.fitMsg(dataSet)); - } + fit{|dataSet, action| + actions[\fit] = [nil, action]; + this.prSendMsg(this.fitMsg(dataSet)); + } - transformMsg{|sourceDataSet, destDataSet| - ^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id); - } + transformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id); + } - transform{|sourceDataSet, destDataSet, action| - actions[\transform] = [nil, action]; - this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet)); - } + transform{|sourceDataSet, destDataSet, action| + actions[\transform] = [nil, action]; + this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet)); + } - transformPointMsg{|sourceBuffer, destBuffer| - ^this.prMakeMsg(\transformPoint,id, - this.prEncodeBuffer(sourceBuffer), - this.prEncodeBuffer(destBuffer), - ["/b_query",destBuffer.asUGenInput] - ); - } + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } - transformPoint{|sourceBuffer, destBuffer, action| - actions[\transformPoint] = [nil,{action.value(destBuffer)}]; - this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); - } + transformPoint{|sourceBuffer, destBuffer, action| + actions[\transformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); + } kr{|trig, inputBuffer,outputBuffer| - ^FluidUMAPQuery.kr(trig, - this, + ^FluidUMAPQuery.kr(trig, + this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer)); - } + } // not implemented cols {|action|} @@ -77,12 +77,12 @@ FluidUMAP : FluidModelObject { } FluidUMAPQuery : FluidRTMultiOutUGen { - *kr{ |trig, model, inputBuffer,outputBuffer| - ^this.multiNew('control',trig, model.asUGenInput, - inputBuffer.asUGenInput, outputBuffer.asUGenInput) - } + *kr{ |trig, model, inputBuffer,outputBuffer| + ^this.multiNew('control',trig, model.asUGenInput, + inputBuffer.asUGenInput, outputBuffer.asUGenInput) + } - init { arg ... theInputs; + init { arg ... theInputs; inputs = theInputs; ^this.initOutputs(1, rate); } diff --git a/release-packaging/Classes/FluidWaveform.sc b/release-packaging/Classes/FluidWaveform.sc index 2e2d5b5..2c1286e 100644 --- a/release-packaging/Classes/FluidWaveform.sc +++ b/release-packaging/Classes/FluidWaveform.sc @@ -433,5 +433,5 @@ FluidWaveform : FluidViewer { }; } - asView { ^view } + asView { ^view } }