From 87e960a0f622c841853c3d4fa25eed3dc0728d71 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 4 Apr 2019 11:30:28 +0100 Subject: [PATCH] all post-refactor post-renaming interface change in classes, help and examples --- release-packaging/Classes/FluidBufCompose.sc | 16 +-- release-packaging/Classes/FluidBufHPSS.sc | 28 ++--- release-packaging/Classes/FluidBufNMF.sc | 28 ++--- .../Classes/FluidBufNoveltySlice.sc | 16 +-- .../Classes/FluidBufOnsetSlice.sc | 16 +-- release-packaging/Classes/FluidBufSines.sc | 22 ++-- .../Classes/FluidBufTransientSlice.sc | 16 +-- .../Classes/FluidBufTransients.sc | 22 ++-- release-packaging/Classes/FluidHPSS.sc | 8 +- release-packaging/Classes/FluidNMFMatch.sc | 13 +- release-packaging/Classes/FluidOnsetSlice.sc | 4 +- release-packaging/Classes/FluidSines.sc | 4 +- .../HelpSource/Classes/FluidBufCompose.schelp | 58 ++++----- .../HelpSource/Classes/FluidBufHPSS.schelp | 73 ++++++----- .../HelpSource/Classes/FluidBufNMF.schelp | 105 ++++++++-------- .../Classes/FluidBufNoveltySlice.schelp | 20 +-- .../Classes/FluidBufOnsetSlice.schelp | 115 ++++++++++++++++++ .../HelpSource/Classes/FluidBufSines.schelp | 22 ++-- .../Classes/FluidBufTransientSlice.schelp | 17 ++- .../Classes/FluidBufTransients.schelp | 16 +-- .../HelpSource/Classes/FluidGain.schelp | 2 +- .../HelpSource/Classes/FluidHPSS.schelp | 50 ++++---- .../HelpSource/Classes/FluidNMFMatch.schelp | 40 +++--- .../HelpSource/Classes/FluidOnsetSlice.schelp | 62 ++++++---- .../HelpSource/Classes/FluidSines.schelp | 6 +- .../Classes/FluidSpectralShape.schelp | 4 +- .../fileiterator-2passes.sc | 42 +++++++ .../buffer_compositing/fileiterator.sc | 15 +-- .../ignore/Examples/nmf/JiT-NMF.scd | 4 +- 29 files changed, 505 insertions(+), 339 deletions(-) create mode 100644 release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp create mode 100644 release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc diff --git a/release-packaging/Classes/FluidBufCompose.sc b/release-packaging/Classes/FluidBufCompose.sc index cdb11d9..9e7c3ce 100644 --- a/release-packaging/Classes/FluidBufCompose.sc +++ b/release-packaging/Classes/FluidBufCompose.sc @@ -1,19 +1,19 @@ FluidBufCompose{ - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, srcGain = 1, dstBufNum, dstStartAt = 0, dstStartChan = 0, dstGain = 0, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action; - srcBufNum = srcBufNum.asUGenInput; - dstBufNum = dstBufNum.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; - if(srcBufNum.isNil) {Error("Invalid Buffer").format(thisMethod.name, this.class.name).throw}; - if(dstBufNum.isNil) {Error("Invalid Buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) {Error("Invalid Buffer").format(thisMethod.name, this.class.name).throw}; + if(destination.isNil) {Error("Invalid Buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufCompose, srcBufNum, startAt, nFrames, startChan, nChans, srcGain, dstBufNum, dstStartAt, dstStartChan, dstGain); + server.sendMsg(\cmd, \BufCompose, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain); server.sync; - dstBufNum = server.cachedBufferAt(dstBufNum); dstBufNum.updateInfo; server.sync; - action.value(dstBufNum); + destination = server.cachedBufferAt(destination); destination.updateInfo; server.sync; + action.value(destination); }; } } diff --git a/release-packaging/Classes/FluidBufHPSS.sc b/release-packaging/Classes/FluidBufHPSS.sc index acef5b6..b0c53ee 100644 --- a/release-packaging/Classes/FluidBufHPSS.sc +++ b/release-packaging/Classes/FluidBufHPSS.sc @@ -1,30 +1,30 @@ FluidBufHPSS{ - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, harmBufNum, percBufNum, resBufNum, hFiltSize = 17, pFiltSize = 31, modeFlag, htf1 = 0.1, hta1 = 0, htf2 = 0.5, hta2 = 0, ptf1 = 0.1, pta1 = 0, ptf2 = 0.5, pta2 = 0, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic, percussive, residual, harmFilterSize = 17, percFilterSize = 31, maskingMode, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, winSize = 1024, hopSize = -1, fftSize = -1, action; var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; - srcBufNum = srcBufNum.asUGenInput; - harmBufNum = harmBufNum.asUGenInput; - percBufNum = percBufNum.asUGenInput; - resBufNum = resBufNum.asUGenInput; + source = source.asUGenInput; + harmonic = harmonic.asUGenInput; + percussive = percussive.asUGenInput; + residual = residual.asUGenInput; - if(srcBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; - harmBufNum = harmBufNum ? -1; - percBufNum = percBufNum ? -1; - resBufNum = resBufNum ? -1; + harmonic = harmonic ? -1; + percussive = percussive ? -1; + residual = residual ? -1; //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) forkIfNeeded{ - server.sendMsg(\cmd, \BufHPSS, srcBufNum, startAt, nFrames, startChan, nChans, harmBufNum, percBufNum, resBufNum, hFiltSize, pFiltSize, modeFlag, htf1, hta1, htf2, hta2, ptf1, pta1, ptf2, pta2, winSize, hopSize, fftSize, maxFFTSize, hFiltSize, pFiltSize); + server.sendMsg(\cmd, \BufHPSS, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, winSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize); server.sync; - if (harmBufNum != -1) {harmBufNum = server.cachedBufferAt(harmBufNum); harmBufNum.updateInfo; server.sync;} {harmBufNum = nil}; - if (percBufNum != -1) {percBufNum = server.cachedBufferAt(percBufNum); percBufNum.updateInfo; server.sync;} {percBufNum = nil}; - if (resBufNum != -1) {resBufNum = server.cachedBufferAt(resBufNum); resBufNum.updateInfo;server.sync;} {resBufNum = nil}; - action.value(harmBufNum, percBufNum, resBufNum); + if (harmonic != -1) {harmonic = server.cachedBufferAt(harmonic); harmonic.updateInfo; server.sync;} {harmonic = nil}; + if (percussive != -1) {percussive = server.cachedBufferAt(percussive); percussive.updateInfo; server.sync;} {percussive = nil}; + if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil}; + action.value(harmonic, percussive, residual); }; } } diff --git a/release-packaging/Classes/FluidBufNMF.sc b/release-packaging/Classes/FluidBufNMF.sc index 596e0d7..92b1f07 100644 --- a/release-packaging/Classes/FluidBufNMF.sc +++ b/release-packaging/Classes/FluidBufNMF.sc @@ -1,27 +1,27 @@ FluidBufNMF { - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, dstBufNum, dictBufNum, dictFlag = 0, actBufNum, actFlag = 0, rank = 1, nIter = 100, sortFlag = 0, winSize = 1024, hopSize = -1, fftSize = -1, winType = 0, randSeed = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, rank = 1, numIter = 100, winSize = 1024, hopSize = -1, fftSize = -1, winType = 0, randSeed = -1, action; - srcBufNum = srcBufNum.asUGenInput; - dstBufNum = dstBufNum.asUGenInput; - dictBufNum = dictBufNum.asUGenInput; - actBufNum = actBufNum.asUGenInput; + source = source.asUGenInput; + destination = destination.asUGenInput; + bases = bases.asUGenInput; + activations = activations.asUGenInput; - if(srcBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; - dstBufNum = dstBufNum ? -1; - dictBufNum = dictBufNum ? -1; - actBufNum = actBufNum ? -1; + destination = destination ? -1; + bases = bases ? -1; + activations = activations ? -1; forkIfNeeded{ - server.sendMsg(\cmd, \BufNMF, srcBufNum, startAt, nFrames, startChan, nChans, dstBufNum, dictBufNum, dictFlag, actBufNum, actFlag, rank, nIter, winSize, hopSize, fftSize); + server.sendMsg(\cmd, \BufNMF, source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, rank, numIter, winSize, hopSize, fftSize); server.sync; - if (dstBufNum != -1) {dstBufNum = server.cachedBufferAt(dstBufNum); dstBufNum.updateInfo; server.sync;} {dstBufNum = nil}; - if (dictBufNum != -1) {dictBufNum = server.cachedBufferAt(dictBufNum); dictBufNum.updateInfo;server.sync;} {dictBufNum = nil}; - if (actBufNum != -1) {actBufNum = server.cachedBufferAt(actBufNum); actBufNum.updateInfo;server.sync;} {actBufNum = nil}; - action.value(dstBufNum,dictBufNum,actBufNum); + if (destination != -1) {destination = server.cachedBufferAt(destination); destination.updateInfo; server.sync;} {destination = nil}; + if (bases != -1) {bases = server.cachedBufferAt(bases); bases.updateInfo; server.sync;} {bases = nil}; + if (activations != -1) {activations = server.cachedBufferAt(activations); activations.updateInfo; server.sync;} {activations = nil}; + action.value(destination, bases, activations); }; } } diff --git a/release-packaging/Classes/FluidBufNoveltySlice.sc b/release-packaging/Classes/FluidBufNoveltySlice.sc index fcb60a9..64bb56d 100644 --- a/release-packaging/Classes/FluidBufNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufNoveltySlice.sc @@ -1,21 +1,21 @@ FluidBufNoveltySlice{ - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, indBufNum, kernSize = 3, thresh = 0.8, filtSize = 1, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, kernSize = 3, threshold = 0.8, filterSize = 1, winSize = 1024, hopSize = -1, fftSize = -1, action; //var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; //ready for when we need it from the RT wrapper - srcBufNum = srcBufNum.asUGenInput; - indBufNum = indBufNum.asUGenInput; + source = source.asUGenInput; + indices = indices.asUGenInput; - if(srcBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; - if(indBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(indices.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufNoveltySlice, srcBufNum, startAt, nFrames, startChan, nChans, indBufNum, kernSize, thresh, filtSize, winSize, hopSize, fftSize); + server.sendMsg(\cmd, \BufNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, kernSize, threshold, filterSize, winSize, hopSize, fftSize); server.sync; - indBufNum = server.cachedBufferAt(indBufNum); indBufNum.updateInfo; server.sync; - action.value(indBufNum); + indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; + action.value(indices); }; } } diff --git a/release-packaging/Classes/FluidBufOnsetSlice.sc b/release-packaging/Classes/FluidBufOnsetSlice.sc index 2aa02eb..795390f 100644 --- a/release-packaging/Classes/FluidBufOnsetSlice.sc +++ b/release-packaging/Classes/FluidBufOnsetSlice.sc @@ -1,13 +1,13 @@ FluidBufOnsetSlice{ - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, indBufNum, function = 0, threshold = 0.1, debounce = 2, filterSize = 5, frameDelta = 0, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, function = 0, threshold = 0.1, debounce = 2, filterSize = 5, frameDelta = 0, winSize = 1024, hopSize = -1, fftSize = -1, action; var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; - srcBufNum = srcBufNum.asUGenInput; - indBufNum = indBufNum.asUGenInput; + source = source.asUGenInput; + indices = indices.asUGenInput; - if(srcBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; - if(indBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(indices.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; @@ -15,10 +15,10 @@ FluidBufOnsetSlice{ //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) forkIfNeeded{ - server.sendMsg(\cmd, \BufOnsetSlice, srcBufNum, startAt, nFrames, startChan, nChans, indBufNum, function, threshold, debounce, filterSize, frameDelta, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufOnsetSlice, source, startFrame, numFrames, startChan, numChans, indices, function, threshold, debounce, filterSize, frameDelta, winSize, hopSize, fftSize, maxFFTSize); server.sync; - indBufNum = server.cachedBufferAt(indBufNum); indBufNum.updateInfo; server.sync; - action.value(indBufNum); + indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; + action.value(indices); }; } } diff --git a/release-packaging/Classes/FluidBufSines.sc b/release-packaging/Classes/FluidBufSines.sc index dd6a96e..2f1a8a3 100644 --- a/release-packaging/Classes/FluidBufSines.sc +++ b/release-packaging/Classes/FluidBufSines.sc @@ -1,27 +1,27 @@ FluidBufSines{ - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, sineBufNum, resBufNum, bw = 76, thresh = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines, residual, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, winSize = 1024, hopSize = -1, fftSize = -1, action; var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; - srcBufNum = srcBufNum.asUGenInput; - sineBufNum = sineBufNum.asUGenInput; - resBufNum = resBufNum.asUGenInput; + source = source.asUGenInput; + sines = sines.asUGenInput; + residual = residual.asUGenInput; - if(srcBufNum.isNil) {Error("Invalid Buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) {Error("Invalid Buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; - sineBufNum = sineBufNum ? -1; - resBufNum = resBufNum ? -1; + sines = sines ? -1; + residual = residual ? -1; //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) forkIfNeeded{ - server.sendMsg(\cmd, \BufSines, srcBufNum, startAt, nFrames, startChan, nChans, sineBufNum, resBufNum, bw, thresh, minTrackLen, magWeight, freqWeight, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufSines, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, winSize, hopSize, fftSize, maxFFTSize); server.sync; - if (sineBufNum != -1) {sineBufNum = server.cachedBufferAt(sineBufNum); sineBufNum.updateInfo; server.sync;} {sineBufNum = nil}; - if (resBufNum != -1) {resBufNum = server.cachedBufferAt(resBufNum); resBufNum.updateInfo;server.sync;} {resBufNum = nil}; - action.value(sineBufNum,resBufNum); + if (sines != -1) {sines = server.cachedBufferAt(sines); sines.updateInfo; server.sync;} {sines = nil}; + if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil}; + action.value(sines, residual); }; } } diff --git a/release-packaging/Classes/FluidBufTransientSlice.sc b/release-packaging/Classes/FluidBufTransientSlice.sc index 42f8007..69e3556 100644 --- a/release-packaging/Classes/FluidBufTransientSlice.sc +++ b/release-packaging/Classes/FluidBufTransientSlice.sc @@ -1,19 +1,19 @@ FluidBufTransientSlice{ - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, indBufNum, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, winSize = 14, debounce = 25, minSlice = 1000, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, winSize = 14, debounce = 25, minSlice = 1000, action; - srcBufNum = srcBufNum.asUGenInput; - indBufNum = indBufNum.asUGenInput; + source = source.asUGenInput; + indices = indices.asUGenInput; - if(srcBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; - if(indBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(indices.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufTransientSlice, srcBufNum, startAt, nFrames, startChan, nChans, indBufNum, order, blockSize, padSize, skew, threshFwd, threshBack, winSize, debounce, minSlice); + server.sendMsg(\cmd, \BufTransientSlice, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, winSize, debounce, minSlice); server.sync; - indBufNum = server.cachedBufferAt(indBufNum); indBufNum.updateInfo; server.sync; - action.value(indBufNum); + indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; + action.value(indices); }; } } diff --git a/release-packaging/Classes/FluidBufTransients.sc b/release-packaging/Classes/FluidBufTransients.sc index 9c6bf7f..ad817cf 100644 --- a/release-packaging/Classes/FluidBufTransients.sc +++ b/release-packaging/Classes/FluidBufTransients.sc @@ -1,22 +1,22 @@ FluidBufTransients { - *process { arg server, srcBufNum, startAt = 0, nFrames = -1, startChan = 0, nChans = -1, transBufNum, resBufNum, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, winSize = 14, debounce = 25, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients, residual, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, winSize = 14, debounce = 25, action; - srcBufNum = srcBufNum.asUGenInput; - transBufNum = transBufNum.asUGenInput; - resBufNum = resBufNum.asUGenInput; + source = source.asUGenInput; + transients = transients.asUGenInput; + residual = residual.asUGenInput; - if(srcBufNum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; server = server ? Server.default; - transBufNum = transBufNum ? -1; - resBufNum = resBufNum ? -1; + transients = transients ? -1; + residual = residual ? -1; forkIfNeeded{ - server.sendMsg(\cmd, \BufTransients, srcBufNum, startAt, nFrames, startChan, nChans, transBufNum, resBufNum, order, blockSize, padSize, skew, threshFwd, threshBack, winSize, debounce); + server.sendMsg(\cmd, \BufTransients, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, winSize, debounce); server.sync; - if (transBufNum != -1) {transBufNum = server.cachedBufferAt(transBufNum); transBufNum.updateInfo; server.sync;} {transBufNum = nil}; - if (resBufNum != -1) {resBufNum = server.cachedBufferAt(resBufNum); resBufNum.updateInfo;server.sync;} {resBufNum = nil}; - action.value(transBufNum,resBufNum); + if (transients != -1) {transients = server.cachedBufferAt(transients); transients.updateInfo; server.sync;} {transients = nil}; + if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil}; + action.value(transients, residual); }; } } diff --git a/release-packaging/Classes/FluidHPSS.sc b/release-packaging/Classes/FluidHPSS.sc index be5bfb6..85c7c8d 100644 --- a/release-packaging/Classes/FluidHPSS.sc +++ b/release-packaging/Classes/FluidHPSS.sc @@ -1,6 +1,6 @@ FluidHPSS : MultiOutUGen { - *ar { arg in = 0, hFiltSize=17, pFiltSize = 31, modeFlag=0, htf1 = 0.1, hta1 = 0, htf2 = 0.5, hta2 = 0, ptf1 = 0.1, pta1 = 0, ptf2 = 0.5, pta2 = 0, winSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384, maxHFlitSize = 101, maxPFiltSize = 101; - ^this.multiNew('audio', in.asAudioRateInput(this), hFiltSize, pFiltSize, modeFlag, htf1, hta1, htf2, hta2, ptf1, pta1, ptf2, pta2, winSize, hopSize, fftSize, maxFFTSize, maxHFlitSize, maxPFiltSize) + *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, winSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384, maxHarmFilterSize = 101, maxPercFilterSize = 101; + ^this.multiNew('audio', in.asAudioRateInput(this), harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, winSize, hopSize, fftSize, maxFFTSize, maxHarmFilterSize, maxPercFilterSize) } init { arg ... theInputs; inputs = theInputs; @@ -16,10 +16,10 @@ FluidHPSS : MultiOutUGen { ^(": maxFFTSize cannot be modulated."); }; if(inputs.at(16).rate != 'scalar') { - ^(": maxHFlitSize cannot be modulated."); + ^(": maxHarmFilterSize cannot be modulated."); }; if(inputs.at(17).rate != 'scalar') { - ^(": maxPFiltSize cannot be modulated."); + ^(": maxPercFilterSize cannot be modulated."); }; ^this.checkValidInputs; } diff --git a/release-packaging/Classes/FluidNMFMatch.sc b/release-packaging/Classes/FluidNMFMatch.sc index 80e91fd..68ef57f 100644 --- a/release-packaging/Classes/FluidNMFMatch.sc +++ b/release-packaging/Classes/FluidNMFMatch.sc @@ -1,7 +1,7 @@ FluidNMFMatch : MultiOutUGen { - *kr { arg in = 0, dictBufNum, maxRank = 1, nIter = 10, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('control', in.asAudioRateInput(this), dictBufNum, maxRank, nIter, winSize, hopSize, fftSize, maxFFTSize); + *kr { arg in = 0, bases, maxRank = 1, numIter = 10, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), bases, maxRank, numIter, winSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; @@ -10,9 +10,12 @@ FluidNMFMatch : MultiOutUGen { } checkInputs { - if(inputs.at(7).rate != 'scalar') { - ^(": maxFFTSize cannot be modulated."); - }; + if(inputs.at(2).rate != 'scalar') { + ^(": maxRank cannot be modulated."); + }; + 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 d969808..b50980a 100644 --- a/release-packaging/Classes/FluidOnsetSlice.sc +++ b/release-packaging/Classes/FluidOnsetSlice.sc @@ -1,6 +1,6 @@ FluidOnsetSlice : UGen { - *ar { arg in = 0, function = 0, thresh = 0.5, debounce = 2, filtSize = 5, frameDelta = 0, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('audio', in.asAudioRateInput(this), function, thresh, debounce, filtSize, winSize, hopSize, frameDelta, fftSize, maxFFTSize) + *ar { arg in = 0, function = 0, threshold = 0.5, debounce = 2, filterSize = 5, frameDelta = 0, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('audio', in.asAudioRateInput(this), function, threshold, debounce, filterSize, frameDelta, winSize, hopSize, fftSize, maxFFTSize) } checkInputs { if(inputs.at(9).rate != 'scalar') { diff --git a/release-packaging/Classes/FluidSines.sc b/release-packaging/Classes/FluidSines.sc index 384e68d..3d2f16a 100644 --- a/release-packaging/Classes/FluidSines.sc +++ b/release-packaging/Classes/FluidSines.sc @@ -1,6 +1,6 @@ FluidSines : MultiOutUGen { - *ar { arg in = 0, bw = 76, thresh = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1.0, winSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize=16384; - ^this.multiNew('audio', in.asAudioRateInput(this), bw, thresh, minTrackLen, magWeight, freqWeight ,winSize, hopSize, fftSize, maxFFTSize) + *ar { arg in = 0, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1.0, winSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize=16384; + ^this.multiNew('audio', in.asAudioRateInput(this), bandwidth, threshold, minTrackLen, magWeight, freqWeight ,winSize, hopSize, fftSize, maxFFTSize) } init { arg ... theInputs; inputs = theInputs; diff --git a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp index 77df911..d713f5f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp @@ -8,7 +8,7 @@ DESCRIPTION:: A FluidBufCompose object provides a flexible utility for combining the contents of buffers on the server. It can be used for thing like mixing down multichannel buffers, or converting from left-right stereo to mid-side. It is used extensively in all the example code of LINK::Guides/FluidDecomposition:: as part of the FluCoMa project. footnote:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: -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 sources 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. +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. @@ -21,38 +21,38 @@ METHOD:: process ARGUMENT:: server The server on which the buffers to be processed are allocated. -ARGUMENT:: srcBufNum +ARGUMENT:: source The bufNum of the source buffer. -ARGUMENT:: startAt +ARGUMENT:: startFrame The starting point (in samples) from which to copy in the source buffer. -ARGUMENT:: nFrames +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:: nChans +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:: srcGain +ARGUMENT:: gain The gain applied to the samples to be copied from the source buffer. -ARGUMENT:: dstBufNum +ARGUMENT:: destination The bufNum of the destination buffer. -ARGUMENT:: dstStartAt +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:: dstStartChan +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:: dstGain +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:: action - A Function to be evaluated once the offline process has finished and dstBufNum instance variables have been updated on the client side. The function will be passed dstBufNum as an argument. + 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:: Nothing, as the destination buffer is declared in the function call. @@ -71,29 +71,29 @@ d = Buffer.new(s); ) // with basic params (basic summing of each full buffer in all dimensions) -FluidBufCompose.process(s, srcBufNum: b.bufnum, dstBufNum: d.bufnum); -FluidBufCompose.process(s, srcBufNum: c.bufnum, dstBufNum: d.bufnum, dstGain: 1.0); +FluidBufCompose.process(s, source: b.bufnum, destination: d.bufnum); +FluidBufCompose.process(s, source: c.bufnum, destination: d.bufnum, destGain: 1.0); d.query; d.play; //constructing a mono buffer, with a quiet punch from the synth, with a choked piano resonance from the left channel d.free; d = Buffer.new(s); -FluidBufCompose.process(s, srcBufNum: b.bufnum, nFrames: 9000, srcGain: 0.5, dstBufNum: d.bufnum); -FluidBufCompose.process(s, srcBufNum: c.bufnum, startAt:30000, nFrames:44100, nChans:1, srcGain:0.9, dstBufNum: d.bufnum, dstGain: 1.0); +FluidBufCompose.process(s, source: b.bufnum, numFrames: 9000, gain: 0.5, destination: d.bufnum); +FluidBufCompose.process(s, source: c.bufnum, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d.bufnum, destGain: 1.0); d.query; d.play; //constructing a stereo buffer, with the end of the mono synth in both channels, with a piano resonance in swapped stereo d.free; d = Buffer.new(s); -FluidBufCompose.process(s, srcBufNum: b.bufnum, startAt: 441000, nChans: 2, srcGain: 0.6, dstBufNum: d.bufnum); -FluidBufCompose.process(s, srcBufNum: c.bufnum, nFrames: 78000, startChan: 1, nChans: 2, srcGain: 0.5, dstStartAt: 22050, dstBufNum: d.bufnum, dstGain: 1.0); +FluidBufCompose.process(s, source: b.bufnum, startFrame: 441000, numChans: 2, gain: 0.6, destination: d.bufnum); +FluidBufCompose.process(s, source: c.bufnum, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d.bufnum, destGain: 1.0); d.query; d.play; //constructing a one second buffer: the first second of each buffer, the mono synth on the right, the piano on the left d.free; d = Buffer.new(s); -FluidBufCompose.process(s, srcBufNum: b.bufnum, nFrames: 44100, nChans: 1, dstStartChan: 1, dstBufNum: d.bufnum); -FluidBufCompose.process(s, srcBufNum: c.bufnum, nFrames:44100, nChans:1, dstBufNum: d.bufnum, dstGain: 1.0); +FluidBufCompose.process(s, source: b.bufnum, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d.bufnum); +FluidBufCompose.process(s, source: c.bufnum, numFrames:44100, numChans:1, destination: d.bufnum, destGain: 1.0); d.query; d.play; :: @@ -112,10 +112,10 @@ f = Buffer.new(s); // encode the mid (in c) and the side (in d) ( -FluidBufCompose.process(s,b.bufnum, nChans: 1, srcGain: -3.0.dbamp, dstBufNum: c.bufnum); -FluidBufCompose.process(s,b.bufnum, nChans: 1, srcGain: -3.0.dbamp, dstBufNum: d.bufnum); -FluidBufCompose.process(s,b.bufnum, nChans: 1, srcGain: -3.0.dbamp, startChan: 1, dstBufNum: c.bufnum, dstGain: 1.0); -FluidBufCompose.process(s,b.bufnum, nChans: 1, srcGain: -3.0.dbamp * -1.0, startChan: 1, dstBufNum: d.bufnum, dstGain: 1.0); +FluidBufCompose.process(s,b.bufnum, numChans: 1, gain: -3.0.dbamp, destination: c.bufnum); +FluidBufCompose.process(s,b.bufnum, numChans: 1, gain: -3.0.dbamp, destination: d.bufnum); +FluidBufCompose.process(s,b.bufnum, numChans: 1, gain: -3.0.dbamp, startChan: 1, destination: c.bufnum, destGain: 1.0); +FluidBufCompose.process(s,b.bufnum, numChans: 1, gain: -3.0.dbamp * -1.0, startChan: 1, destination: d.bufnum, destGain: 1.0); ) // (optional) compare auraly the stereo with the MS @@ -131,7 +131,7 @@ b.play; e.free; e = Buffer.new(s); ( [1.0, -1.0].do({ arg x,y; - FluidBufCompose.process(s, d.bufnum, srcGain: x, dstStartAt: y, dstBufNum: e.bufnum, dstGain: 1.0); + FluidBufCompose.process(s, d.bufnum, gain: x, destStartFrame: y, destination: e.bufnum, destGain: 1.0); }); ) @@ -139,7 +139,7 @@ e.free; e = Buffer.new(s); e.free; e = Buffer.new(s); ( [0.8, -0.32, -0.24, -0.16, -0.08].do({ arg x,y; - FluidBufCompose.process(s, d.bufnum, srcGain: x, dstStartAt: y, dstBufNum: e.bufnum, dstGain: 1.0); + FluidBufCompose.process(s, d.bufnum, gain: x, destStartFrame: y, destination: e.bufnum, destGain: 1.0); }); ) @@ -147,7 +147,7 @@ e.free; e = Buffer.new(s); e.free; e = Buffer.new(s); ( [0.982494, -0.066859, -0.064358, -0.061897, -0.059477, -0.057098, -0.054761, -0.052466, -0.050215, -0.048007, -0.045843, -0.043724, -0.041649, -0.03962, -0.037636, -0.035697, -0.033805, -0.031959, -0.030159, -0.028406, -0.026699, -0.025038, -0.023425, -0.021857, -0.020337].do({ arg x,y; - FluidBufCompose.process(s, d.bufnum, srcGain: x, dstStartAt: y, dstBufNum: e.bufnum, dstGain: 1.0); + FluidBufCompose.process(s, d.bufnum, gain: x, destStartFrame: y, destination: e.bufnum, destGain: 1.0); }); ) @@ -158,9 +158,9 @@ e.play; // decode the MS back to stereo ( -FluidBufCompose.process(s,c.bufnum, nChans: 2, srcGain: -3.0.dbamp, dstBufNum: f.bufnum); -FluidBufCompose.process(s,e.bufnum, srcGain: -3.0.dbamp, dstBufNum: f.bufnum, dstGain: 1.0); -FluidBufCompose.process(s,e.bufnum, srcGain: -3.0.dbamp * -1.0, dstBufNum: f.bufnum, dstStartChan: 1, dstGain: 1.0); +FluidBufCompose.process(s,c.bufnum, numChans: 2, gain: -3.0.dbamp, destination: f.bufnum); +FluidBufCompose.process(s,e.bufnum, gain: -3.0.dbamp, destination: f.bufnum, destGain: 1.0); +FluidBufCompose.process(s,e.bufnum, gain: -3.0.dbamp * -1.0, destination: f.bufnum, destStartChan: 1, destGain: 1.0); ) // query and play diff --git a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp index 20db545..59cd518 100644 --- a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp @@ -14,7 +14,7 @@ Driedger, Jonathan, Meinard Uller, and Sascha Disch. 2014. ‘Extending Harmonic 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 modeFlag below.:: + ## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.:: It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899). @@ -31,68 +31,68 @@ This is the method that calls for the HPSS to be calculated on a given source bu ARGUMENT:: server The server on which the buffers to be processed are allocated. -ARGUMENT:: srcBufNum +ARGUMENT:: source The index of the buffer to use as the source material. The channels of multichannel buffers will be processed sequentially. -ARGUMENT:: startAt - Where in the srcBuf should the NMF process start, in samples. +ARGUMENT:: startFrame + Where in the srcBuf should the HPSS process start, in samples. -ARGUMENT:: nFrames +ARGUMENT:: numFrames How many frames should be processed. ARGUMENT:: startChan For multichannel srcBuf, which channel to start processing at. -ARGUMENT:: nChans +ARGUMENT:: numChans For multichannel srcBuf, how many channels should be processed. -ARGUMENT:: harmBufNum +ARGUMENT:: harmonic The index of the buffer where the extracted harmonic component will be reconstructed. -ARGUMENT:: percBufNum +ARGUMENT:: percussive The index of the buffer where the extracted percussive component will be reconstructed. -ARGUMENT:: resBufNum +ARGUMENT:: residual The index of the buffer where the residual component will be reconstructed in mode 2. -ARGUMENT:: hFiltSize +ARGUMENT:: harmFilterSize The size, in spectral frames, of the median filter for the harmonic component. Must be an odd number, >= 3. -ARGUMENT:: pFiltSize +ARGUMENT:: percFilterSize The size, in spectral bins, of the median filter for the percussive component. Must be an odd number, >=3 -ARGUMENT:: modeFlag +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 htf1, hta1, htf2, hta2, 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. + ## 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:: htf1 +ARGUMENT:: harmThreshFreq1 In modes 1 and 2, the frequency of the low part of the threshold for the harmonic filter (0-1) -ARGUMENT:: hta1 - In modes 1 and 2, the threshold of the low part for the harmonic filter. That threshold applies to all frequencies up to htf1: 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:: 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:: htf2 +ARGUMENT:: harmThreshFreq2 In modes 1 and 2, the frequency of the hight part of the threshold for the harmonic filter. (0-1) -ARGUMENT:: hta2 - In modes 1 and 2, the threshold of the high part for the harmonic filter. That threshold applies to all frequencies above htf2. The threshold between htf1 and htf2 is interpolated between hta1 and hta2. 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:: 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:: ptf1 +ARGUMENT:: percThreshFreq1 In mode 2, the frequency of the low part of the threshold for the percussive filter. (0-1) -ARGUMENT:: pta1 - In mode 2, the threshold of the low part for the percussive filter. That threshold applies to all frequencies up to ptf1. 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:: 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:: ptf2 +ARGUMENT:: percThreshFreq2 In mode 2, the frequency of the hight part of the threshold for the percussive filter. (0-1) -ARGUMENT:: pta2 - In mode 2, the threshold of the high part for the percussive filter. That threshold applies to all frequencies above ptf2. The threshold between ptf1 and ptf2 is interpolated between pta1 and pta2. 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:: 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:: winSize 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 @@ -104,7 +104,7 @@ ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. 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 [harmBufNum, percBufNum, resBufNum] as an argument. + 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:: Nothing, as the various destination buffers are declared in the function call. @@ -112,7 +112,7 @@ RETURNS:: 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 modeFlag 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). + 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:: @@ -129,17 +129,15 @@ code:: ( Routine{ t = Main.elapsedTime; - FluidBufHPSS.process(s, b.bufnum, harmBufNum: c.bufnum, percBufNum: d.bufnum); + FluidBufHPSS.process(s, b.bufnum, harmonic: c.bufnum, percussive: d.bufnum); s.sync; (Main.elapsedTime - t).postln; }.play ) -//query and play the harmonic -c.query; +//play the harmonic c.play; -//querry and play the percussive -d.query; +//play the percussive d.play; //nullsumming tests @@ -149,20 +147,17 @@ d.play; ( Routine{ t = Main.elapsedTime; - FluidBufHPSS.process(s, b.bufnum, harmBufNum: c.bufnum, percBufNum: d.bufnum, resBufNum:e.bufnum, hFiltSize:31, modeFlag:2, htf1: 0.005, hta1: 7.5, htf2: 0.168, hta2: 7.5, ptf1: 0.004, pta1: 26.5, ptf2: 0.152, pta2: 26.5); + 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,winSize:4096,hopSize:512); s.sync; (Main.elapsedTime - t).postln; }.play ) -//query and play the harmonic -c.query; +//play the harmonic c.play; -//query and play the percussive -d.query; +//play the percussive d.play; -//query and play the residual -e.query; +//play the residual e.play; //still nullsumming diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index 4a84616..06d5f08 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -8,19 +8,19 @@ DESCRIPTION:: The FluidBufNMF object decomposes the spectrum of a sound into a number of components using Non-Negative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. ::. 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. -The algorithm takes a buffer in and divides it into a number of components, determined by the rank argument. It works iteratively, by trying to find a combination of spectral templates ('dictionaries') 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. +The algorithm takes a buffer in and divides it into a number of components, determined by the rank 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. 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 dictionary in NMF lingo); + ## 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 dictionaries 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 dictionary and an activation will yield a synthetic magnitude spectrogram of a component (which could be reconsructed, given some phase informaiton from somewhere). +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 dictFlag and actFlag 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 dictionaries and activations set to 2 doesn't make sense, so the object will complain. +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. If supplying pre-formed data, it's up to the user to make sure that the supplied buffers are the right size: LIST:: -## dictionaries must be STRONG::(fft size / 2) + 1:: frames and STRONG::(rank * input channels):: channels +## bases must be STRONG::(fft size / 2) + 1:: frames and STRONG::(rank * input channels):: channels ## activations must be STRONG::(input frames / hopSize) + 1:: frames and STRONG::(rank * input channels):: channels :: @@ -41,39 +41,39 @@ This is the method that calls for the factorisation to be calculated on a given ARGUMENT:: server The server on which the buffers to be processed are allocated. -ARGUMENT:: srcBufNum +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:: startAt +ARGUMENT:: startFrame Where in the srcBuf should the NMF process start, in sample. -ARGUMENT:: nFrames +ARGUMENT:: numFrames How many frames should be processed. ARGUMENT:: startChan For multichannel srcBuf, which channel should be processed first. -ARGUMENT:: nChans +ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. -ARGUMENT:: dstBufNum +ARGUMENT:: destination The index of the buffer where the different reconstructed ranks will be reconstructed. The buffer will be resized to STRONG::rank * numChannelsProcessed:: channels and STRONG::sourceDuration:: lenght. If STRONG::nil:: is provided, the reconstruction will not happen. -ARGUMENT:: dictBufNum - The index of the buffer where the different dictionaries will be written to and/or read from: the behaviour is set in the following argument. If STRONG::nil:: is provided, no dictionary will be returned. +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 STRONG::nil:: is provided, no bases will be returned. -ARGUMENT:: dictFlag - This flag decides of how the dictionnary buffer passed as the previous argument is treated. +ARGUMENT:: basesMode + This flag decides of how the basis buffer passed as the previous argument is treated. table:: - ## 0 || The dictionaries are seeded randomly, and the resulting ones will be written after the process in the passed buffer. The buffer is resized to STRONG::rank * numChannelsProcessed:: channels and STRONG::(fftSize / 2 + 1):: lenght. - ## 1 || The passed buffer is considered as seed for the dictionaries. Its dimensions should match the values above. The resulting dictionaries will replace the seed ones. - ## 2 || The passed buffer is considered as a template for the dictionaries, and will therefore not change. Its dictionaries should match the values above. + ## 0 || The bases are seeded randomly, and the resulting ones will be written after the process in the passed buffer. The buffer is resized to STRONG::rank * numChannelsProcessed:: channels and STRONG::(fftSize / 2 + 1):: lenght. + ## 1 || The passed buffer is considered as seed for the bases. Its dimensions should match the values above. The resulting bases will replace the seed ones. + ## 2 || The passed buffer is considered as a template for the bases, and will therefore not change. Its bases should match the values above. :: -ARGUMENT:: actBufNum +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 STRONG::nil:: is provided, no activation will be returned. -ARGUMENT:: actFlag +ARGUMENT:: actMode This flag decides of how the activation buffer passed as the previous argument is treated. table:: ## 0 || The activations are seeded randomly, and the resulting ones will be written after the process in the passed buffer. The buffer is resized to STRONG::rank * numChannelsProcessed:: channels and STRONG::(sourceDuration / hopsize + 1):: lenght. @@ -84,12 +84,9 @@ ARGUMENT:: actFlag ARGUMENT:: rank The number of elements the NMF algorithm will try to divide the spectrogram of the source in. -ARGUMENT:: nIter +ARGUMENT:: numIter 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. -ARGUMENT:: sortFlag - This allows to choose between the different methods of sorting the ranks in order to get similar sonic qualities on a given rank (not implemented yet) - ARGUMENT:: winSize 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. http://www.subsurfwiki.org/wiki/Gabor_uncertainty @@ -106,7 +103,7 @@ ARGUMENT:: randSeed The NMF process needs to seed its starting point. If specified, the same values will be used. The default of -1 will randomly assign them. (not implemented yet) 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 [dstBufNum, dictBufNum, actBufNum] as an argument. + 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, bases, activations] as an argument. RETURNS:: Nothing, as the various destination buffers are declared in the function call. @@ -131,8 +128,8 @@ Routine { b.sine2([500],[1], false, false); c.sine2([5000],[1],false, false); s.sync; - FluidBufCompose.process(s,b.bufnum, dstBufNum:d.bufnum); - FluidBufCompose.process(s,c.bufnum, dstStartAt:44100, dstBufNum:d.bufnum, dstGain:1); + FluidBufCompose.process(s,b.bufnum, destination:d.bufnum); + FluidBufCompose.process(s,c.bufnum, destStartFrame:44100, destination:d.bufnum, destGain:1); s.sync; d.query; }.play; @@ -145,7 +142,7 @@ d.play //////(beware !!!! loud!!!) ( // separate them in 2 ranks Routine { - FluidBufNMF.process(s, d.bufnum, dstBufNum:e.bufnum, dictBufNum: f.bufnum, actBufNum:g.bufnum, rank:2); + FluidBufNMF.process(s, d.bufnum, destination:e.bufnum, bases: f.bufnum, activations:g.bufnum, rank:2); s.sync; e.query; f.query; @@ -156,7 +153,7 @@ Routine { // look at the resynthesised separated signal e.plot; -// look at the dictionaries signal for 2 spikes +// look at the bases signal for 2 spikes f.plot; // look at the activations @@ -177,26 +174,20 @@ y = Buffer.new(s); ~fft_size = 1024; ~frame_size = 512; ~hop_size = 256; -~which_rank = 3; +~which_rank = 1; ) // matrix factorisation, requesting everything ( Routine{ t = Main.elapsedTime; - FluidBufNMF.process(s,b.bufnum, 0,-1,0,-1,c.bufnum,x.bufnum,0,y.bufnum,0,5,100,0,~frame_size,~hop_size,~fft_size); + FluidBufNMF.process(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size); s.sync; (Main.elapsedTime - t).postln; - s.sync; - c.query; - s.sync; - x.query; - s.sync; - y.query; }.play ) -//look at the resynthesised ranks, the dictionaries and the activations +//look at the resynthesised ranks, the bases and the activations c.plot;x.plot; y.plot; //null test of the sum of sources {(PlayBuf.ar(5,c.bufnum,doneAction:2).sum)+(-1*PlayBuf.ar(1,b.bufnum,doneAction:2))}.play @@ -207,7 +198,7 @@ c.plot;x.plot; y.plot; //play a single source {PlayBuf.ar(5,c.bufnum,doneAction:2)[~which_rank].dup}.play -//play noise using one of the dictionaries as filter. +//play noise using one of the bases as filter. ( { var chain; @@ -239,7 +230,7 @@ c.plot;x.plot; y.plot; ) :: -STRONG::Fixed Dictionnaries:: The process can be trained, and the learnt dictionaries or activations can be used as templates. +STRONG::Fixed Bases: The process can be trained, and the learnt bases or activations can be used as templates.:: CODE:: @@ -263,28 +254,28 @@ Routine { // find the rank that has the picking sound by changing which channel to listen to ( - ~element = 4; + ~element = 1; {PlayBuf.ar(10,c.bufnum)[~element]}.play ) -// copy all the other ranks on itself and the picking dictionnary as the sole component of the 1st channel +// copy all the other ranks on itself and the picking basis as the sole component of the 1st channel ( Routine{ z = (0..9); - FluidBufCompose.process(s, x.bufnum, startChan: z.removeAt(~element), nChans: 1, dstBufNum: e.bufnum); + FluidBufCompose.process(s, x.bufnum, startChan: z.removeAt(~element), numChans: 1, destination: e.bufnum); s.sync; e.query; s.sync; - z.do({|chan| FluidBufCompose.process(s, x.bufnum, startChan:chan, nChans: 1, dstStartChan: 1, dstBufNum: e.bufnum, dstGain:1)}); + z.do({|chan| FluidBufCompose.process(s, x.bufnum, startChan:chan, numChans: 1, destStartChan: 1, destination: e.bufnum, destGain:1)}); s.sync; e.query; }.play; ) -//process the whole file, splitting it with the 2 trained dictionnaries +//process the whole file, splitting it with the 2 trained bases ( Routine{ - FluidBufNMF.process(s, b.bufnum, dstBufNum: c.bufnum, dictBufNum: e.bufnum, dictFlag: 2, actBufNum: y.bufnum, rank:2); + FluidBufNMF.process(s, b, destination: c, bases: e, basesMode: 2, activations: y, rank:2); s.sync; c.query; }.play; @@ -297,7 +288,7 @@ c.play {(PlayBuf.ar(2,c.bufnum,doneAction:2).sum)-(PlayBuf.ar(1,b.bufnum,doneAction:2))}.play :: -STRONG::Updating Dictionnaries:: The process can update dictionaries provided as seed. +STRONG::Updating Bases: The process can update bases provided as seed.:: CODE:: ( @@ -316,8 +307,8 @@ Routine { b.sine2([500],[1], false, false); c.sine2([5000],[1],false, false); s.sync; - FluidBufCompose.process(s,b.bufnum, dstBufNum:d.bufnum); - FluidBufCompose.process(s,c.bufnum, dstStartAt:44100, dstBufNum:d.bufnum, dstGain:1); + FluidBufCompose.process(s,b.bufnum, destination:d.bufnum); + FluidBufCompose.process(s,c.bufnum, destStartFrame:44100, destination:d.bufnum, destGain:1); s.sync; d.query; }.play; @@ -328,7 +319,7 @@ d.plot d.play //////(beware !!!! loud!!!) ( -//make a seeding dictionary of 3 ranks: +//make a seeding basis of 3 ranks: var highpass, lowpass, direct; highpass = Array.fill(513,{|i| (i < 50).asInteger}); lowpass = 1 - highpass; @@ -336,14 +327,14 @@ direct = Array.fill(513,0.1); e.setn(0,[highpass, lowpass, direct].flop.flat); ) -//check the dictionary: a steep lowpass, a steep highpass, and a small DC +//check the basis: a steep lowpass, a steep highpass, and a small DC e.plot e.query ( -// use the seeding dictionary, without updating +// use the seeding basis, without updating Routine { - FluidBufNMF.process(s, d.bufnum, dstBufNum:f.bufnum, dictBufNum: e.bufnum, dictFlag: 2, actBufNum:g.bufnum, rank:3); + FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 2, activations:g, rank:3); s.sync; e.query; f.query; @@ -354,16 +345,16 @@ Routine { // look at the resynthesised separated signal f.plot; -// look at the dictionaries that have not changed +// look at the bases that have not changed e.plot; // look at the activations g.plot; ( -// use the seeding dictionary, with updating this time +// use the seeding bases, with updating this time Routine { - FluidBufNMF.process(s, d.bufnum, dstBufNum:f.bufnum, dictBufNum: e.bufnum, dictFlag: 1, actBufNum:g.bufnum, rank:3); + FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 1, activations:g, rank:3); s.sync; e.query; f.query; @@ -374,9 +365,9 @@ Routine { // look at the resynthesised separated signal f.plot; -// look at the dictionaries that have now updated in place (with the 3rd channel being more focused +// 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 rank at transitions) g.plot; -:: \ No newline at end of file +:: diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp index 4d992b3..6c25cc3 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp @@ -18,31 +18,31 @@ This is the method that calls for the slicing to be calculated on a given source ARGUMENT:: server The server on which the buffers to be processed are allocated. -ARGUMENT:: srcBufNum +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:: startAt +ARGUMENT:: startFrame Where in the srcBuf should the slicing process start, in sample. -ARGUMENT:: nFrames +ARGUMENT:: numFrames How many frames should be processed. ARGUMENT:: startChan For multichannel srcBuf, which channel should be processed. -ARGUMENT:: nChans +ARGUMENT:: numChans For multichannel srcBuf, how many channel should be summed. -ARGUMENT:: indBufNum +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:: kernSize 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:: thresh +ARGUMENT:: threshold The normalised threshold, between 0 an 1, on the novelty curve to consider it a segmentation point. -ARGUMENT:: filtSize +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:: winSize @@ -55,7 +55,7 @@ ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indBufNum instance variables have been updated on the client side. The function will be passed indBufNum as an argument. + A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument. RETURNS:: Nothing, as the various destination buffers are declared in the function call. @@ -74,7 +74,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufNoveltySlice.process(s,b.bufnum, indBufNum: c.bufnum, thresh:0.6); + FluidBufNoveltySlice.process(s,b.bufnum, indices: c.bufnum, threshold:0.6); s.sync; (Main.elapsedTime - t).postln; }.play @@ -108,7 +108,7 @@ c = Buffer.new(s); ) // process with a given filterSize -FluidBufNoveltySlice.process(s,b.bufnum, indBufNum: c.bufnum, kernSize:31, thresh:0.3, filtSize:0) +FluidBufNoveltySlice.process(s,b.bufnum, indices: c.bufnum, kernSize:31, threshold:0.3, filterSize:0) //check the number of slices: it is the number of frames in the transBuf minus the boundary index. c.query; diff --git a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp new file mode 100644 index 0000000..ddcbcb4 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp @@ -0,0 +1,115 @@ +TITLE:: FluidBufOnsetSlice +SUMMARY:: Spectral Difference-Based Audio Buffer Slicer +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition + +DESCRIPTION:: +This class implements many spectral-based onset detection functions, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object yet not as offline processes. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: + +The process will return a buffer which contains indices (in sample) of estimated starting points of different slices. + +CLASSMETHODS:: + +METHOD:: process + 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:: function + The function 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 function, from 0 upwards. + +ARGUMENT:: debounce + 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 functions (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:: winSize + 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 hope 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 winSize (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:: 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:: + Nothing, as the various destination buffers are declared in the function call. + +EXAMPLES:: + +CODE:: +( +//prep some buffers +b = Buffer.read(s,File.realpath(FluidBufOnsetSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +c = Buffer.new(s); +) + +( +// with basic params +Routine{ + t = Main.elapsedTime; + FluidBufOnsetSlice.process(s,b.bufnum, indices: c.bufnum, threshold:0.5); + s.sync; + (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.bufnum, + Phasor.ar(0,1, + BufRd.kr(1, c.bufnum, + MouseX.kr(0, BufFrames.kr(c.bufnum) - 1), 0, 1), + BufRd.kr(1, c.bufnum, + MouseX.kr(1, BufFrames.kr(c.bufnum)), 0, 1), + BufRd.kr(1,c.bufnum, + MouseX.kr(0, BufFrames.kr(c.bufnum) - 1), 0, 1)), 0, 1); + }.play; +) +:: + diff --git a/release-packaging/HelpSource/Classes/FluidBufSines.schelp b/release-packaging/HelpSource/Classes/FluidBufSines.schelp index 812787a..348571b 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSines.schelp @@ -21,31 +21,31 @@ METHOD:: process ARGUMENT:: server The server on which the buffers to be processed are allocated. -ARGUMENT:: srcBufNum - 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:: 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:: startAt +ARGUMENT:: startFrame Where in the srcBuf should the process start, in sample. -ARGUMENT:: nFrames +ARGUMENT:: numFrames How many frames should be processed. ARGUMENT:: startChan For multichannel srcBuf, which channel should be processed first. -ARGUMENT:: nChans +ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. -ARGUMENT:: sineBufNum +ARGUMENT:: sines The index of the buffer where the extracted sinusoidal component will be reconstructed. -ARGUMENT:: resBufNum +ARGUMENT:: residual The index of the buffer where the residual of the sinusoidal component will be reconstructed. -ARGUMENT:: bw +ARGUMENT:: bandwidth The width in bins of the fragment of the fft window that is considered a normal deviation for a potential continuous sinusoidal track. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. -ARGUMENT:: thresh +ARGUMENT:: threshold The normalised threshold, between 0 an 1, to consider a peak as a sinusoidal component from the normalized cross-correlation. ARGUMENT:: minTrackLen @@ -67,7 +67,7 @@ ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. 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 [sineBufNum, resBufNum] as an argument. + 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:: Nothing, as the various destination buffers are declared in the function call. @@ -86,7 +86,7 @@ d = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSines.process(s, b.bufnum, sineBufNum: c.bufnum, resBufNum:d.bufnum); + FluidBufSines.process(s, b.bufnum, sines: c.bufnum, residual:d.bufnum); s.sync; (Main.elapsedTime - t).postln; }.play diff --git a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp index 869b062..40a7400 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp @@ -18,22 +18,22 @@ METHOD:: process ARGUMENT:: server The server on which the buffers to be processed are allocated. -ARGUMENT:: srcBufNum +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:: startAt +ARGUMENT:: startFrame Where in the srcBuf should the slicing process start, in sample. -ARGUMENT:: nFrames +ARGUMENT:: numFrames How many frames should be processed. ARGUMENT:: startChan For multichannel srcBuf, which channel should be processed. -ARGUMENT:: nChans +ARGUMENT:: numChans For multichannel srcBuf, how many channel should be summed. -ARGUMENT:: indBufNum +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 @@ -64,7 +64,7 @@ ARGUMENT:: minSlice The minimum duration of a slice in samples. ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indBufNum instance variables have been updated on the client side. The function will be passed indBufNum as an argument. + A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument. RETURNS:: Nothing, as the destination buffer is declared in the function call. @@ -83,7 +83,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b.bufnum, transBufNum:c.bufnum); + FluidBufTransientSlice.process(s,b, indices:c); s.sync; (Main.elapsedTime - t).postln; }.play @@ -106,12 +106,11 @@ c.query; }.play; ) - // with everything changed to make it much better, at the cost of computation time (only 10 seconds are processed here) ( Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b.bufnum, 0, 220500, 0, 1, c.bufnum, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410); + FluidBufTransientSlice.process(s,b, 0, 220500, 0, 1, c, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410); s.sync; (Main.elapsedTime - t).postln; }.play diff --git a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp index c0ebc2d..9556e34 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp @@ -23,25 +23,25 @@ This is the method that calls for the transient extraction to be performed on a ARGUMENT:: server The server on which the buffers to be processed are allocated. -ARGUMENT:: srcBufNum +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:: startAt +ARGUMENT:: startFrame Where in the srcBuf should the NMF process start, in sample. -ARGUMENT:: nFrames +ARGUMENT:: numFrames How many frames should be processed. ARGUMENT:: startChan For multichannel srcBuf, which channel should be processed first. -ARGUMENT:: nChans +ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. -ARGUMENT:: transBufNum +ARGUMENT:: transients The index of the buffer where the extracted transient component will be reconstructed. -ARGUMENT:: resBufNum +ARGUMENT:: residual The index of the buffer where the estimated continuous component will be reconstructed. ARGUMENT:: order @@ -69,7 +69,7 @@ ARGUMENT:: debounce The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. 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 [transBufNum, resBufNum] as an argument. + 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:: Nothing, as the various destination buffers are declared in the function call. @@ -88,7 +88,7 @@ d = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s,b.bufnum, transBufNum:c.bufnum, resBufNum:d.bufnum); + FluidBufTransients.process(s,b.bufnum, transients:c.bufnum, residual:d.bufnum); s.sync; (Main.elapsedTime - t).postln; }.play diff --git a/release-packaging/HelpSource/Classes/FluidGain.schelp b/release-packaging/HelpSource/Classes/FluidGain.schelp index cb67f39..18f4d61 100644 --- a/release-packaging/HelpSource/Classes/FluidGain.schelp +++ b/release-packaging/HelpSource/Classes/FluidGain.schelp @@ -24,7 +24,7 @@ RETURNS:: EXAMPLES:: - Summing with the inverse (gain of -1) with a delay of the latency gives us CPU-expensive silence. + 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 :: diff --git a/release-packaging/HelpSource/Classes/FluidHPSS.schelp b/release-packaging/HelpSource/Classes/FluidHPSS.schelp index 1c25d29..0d0b985 100644 --- a/release-packaging/HelpSource/Classes/FluidHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidHPSS.schelp @@ -12,7 +12,7 @@ Driedger, Jonathan, Meinard Uller, and Sascha Disch. 2014. ‘Extending Harmonic 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 modeFlag below.:: + ## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.:: It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899). @@ -27,44 +27,44 @@ METHOD:: ar ARGUMENT:: in The input to be processed. -ARGUMENT:: hFiltSize +ARGUMENT:: harmFilterSize The size, in spectral frames, of the median filter for the harmonic component. Must be an odd number, >= 3. -ARGUMENT:: pFiltSize +ARGUMENT:: percFilterSize The size, in spectral bins, of the median filter for the percussive component. Must be an odd number, >=3 -ARGUMENT:: modeFlag +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 htf1, hta1, htf2, hta2, 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. + ## 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:: htf1 +ARGUMENT:: harmThreshFreq1 In modes 1 and 2, the frequency of the low part of the threshold for the harmonic filter (0-1) -ARGUMENT:: hta1 - In modes 1 and 2, the threshold of the low part for the harmonic filter. That threshold applies to all frequencies up to htf1: 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:: 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:: htf2 +ARGUMENT:: harmThreshFreq2 In modes 1 and 2, the frequency of the hight part of the threshold for the harmonic filter. (0-1) -ARGUMENT:: hta2 - In modes 1 and 2, the threshold of the high part for the harmonic filter. That threshold applies to all frequencies above htf2. The threshold between htf1 and htf2 is interpolated between hta1 and hta2. 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:: 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:: ptf1 +ARGUMENT:: percThreshFreq1 In mode 2, the frequency of the low part of the threshold for the percussive filter. (0-1) -ARGUMENT:: pta1 - In mode 2, the threshold of the low part for the percussive filter. That threshold applies to all frequencies up to ptf1. 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:: 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:: ptf2 +ARGUMENT:: percThreshFreq2 In mode 2, the frequency of the hight part of the threshold for the percussive filter. (0-1) -ARGUMENT:: pta2 - In mode 2, the threshold of the high part for the percussive filter. That threshold applies to all frequencies above ptf2. The threshold between ptf1 and ptf2 is interpolated between pta1 and pta2. 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:: 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:: winSize 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 @@ -78,21 +78,21 @@ ARGUMENT:: fftSize ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. -ARGUMENT::maxHFlitSize - How large can the harmonic filter be modulated to (hFiltSize), 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:: maxPFiltSize - How large can the percussive filter be modulated to (pFiltSize), 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 ((hFiltSize - 1) * hopSize) + winSize) samples. + 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) + winSize) 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 modeFlag 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). + 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:: @@ -102,7 +102,7 @@ CODE:: b = Buffer.read(s,File.realpath(FluidHPSS.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav"); // run with basic parameters (left is harmonic, right is percussive) -{FluidHPSS.ar(PlayBuf.ar(1,b.bufnum,loop:1),maxFFTSize:LFNoise0.kr())}.play +{FluidHPSS.ar(PlayBuf.ar(1,b.bufnum,loop:1))}.play // run in mode 1 {FluidHPSS.ar(PlayBuf.ar(1,b.bufnum,loop:1),17,51,1,0.05,40,0.1,-40)}.play @@ -115,6 +115,6 @@ b = Buffer.read(s,File.realpath(FluidHPSS.class.filenameSymbol).dirname.withTrai // the residual stream {FluidHPSS.ar(PlayBuf.ar(1,b.bufnum,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 ((hFiltSize - 1) * hopSize) + winSize) samples +// null test (the process add a latency of ((harmFilterSize - 1) * hopSize) + winSize) samples {var sig = PlayBuf.ar(1,b.bufnum,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/FluidNMFMatch.schelp b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp index 4d7f1e7..20fded6 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp @@ -1,5 +1,5 @@ TITLE:: FluidNMFMatch -SUMMARY:: Real-Time Non-Negative Matrix Factorisation with Fixed Dictionaries +SUMMARY:: Real-Time Non-Negative Matrix Factorisation with Fixed Bases CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufNMF @@ -20,18 +20,18 @@ FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. f 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::maxRank:: . If the dictionary buffer has fewer than maxRank channels, the remaining outputs will be zeroed. +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::maxRank:: . If the bases buffer has fewer than maxRank channels, the remaining outputs will be zeroed. ARGUMENT:: in The signal input to the factorisation process. -ARGUMENT:: dictBufNum - The server index of the buffer containing the different dictionaries that the input signal will be matched against. Dictionaries must be STRONG::(fft size / 2) + 1:: frames. If the buffer has more than STRONG::maxRank:: channels, the excess will be ignored. +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::maxRank:: channels, the excess will be ignored. ARGUMENT::maxRank 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:: nIter +ARGUMENT:: numIter 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:: winSize @@ -47,7 +47,7 @@ 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 dictionary component the activation amount. + A multichannel kr output, giving for each basis component the activation amount. EXAMPLES:: @@ -67,8 +67,8 @@ Routine { b.sine2([500],[1], false, false); c.sine2([5000],[1],false, false); s.sync; - FluidBufCompose.process(s,b.bufnum, dstBufNum:d.bufnum); - FluidBufCompose.process(s,c.bufnum, dstStartAt:44100, dstBufNum:d.bufnum, dstGain:1); + FluidBufCompose.process(s,b.bufnum, destination:d.bufnum); + FluidBufCompose.process(s,c.bufnum, destStartFrame:44100, destination:d.bufnum, destGain:1); s.sync; d.query; }.play; @@ -81,7 +81,7 @@ d.play //////(beware !!!! loud!!!) ( // separate them in 2 ranks Routine { - FluidBufNMF.process(s, d.bufnum, dictBufNum: e.bufnum, rank:2); + FluidBufNMF.process(s, d.bufnum, bases: e.bufnum, rank:2); s.sync; e.query; }.play @@ -125,15 +125,15 @@ Routine { {PlayBuf.ar(10,c.bufnum)[~element]}.play ) -// copy all the other ranks on itself and the picking dictionnary as the sole component of the 1st channel +// copy all the other ranks on itself and the picking basis as the sole component of the 1st channel ( Routine{ z = (0..9); - FluidBufCompose.process(s, x.bufnum, startChan: z.removeAt(~element), nChans: 1, dstBufNum: e.bufnum); + FluidBufCompose.process(s, x.bufnum, startChan: z.removeAt(~element), numChans: 1, destination: e.bufnum); s.sync; e.query; s.sync; - z.do({|chan| FluidBufCompose.process(s, x.bufnum, startChan:chan, nChans: 1, dstStartChan: 1, dstBufNum: e.bufnum, dstGain:1)}); + z.do({|chan| FluidBufCompose.process(s, x.bufnum, startChan:chan, numChans: 1, destStartChan: 1, destination: e.bufnum, destGain:1)}); s.sync; e.query; }.play; @@ -141,7 +141,7 @@ Routine{ e.plot; -//using this trained dictionary we can see the envelop (activations) of each rank +//using this trained basis we can see the envelop (activations) of each rank {FluidNMFMatch.kr(PlayBuf.ar(1,b.bufnum),e.bufnum,2,fftSize:2048)}.plot(1); // the left/top activations are before, the pick before the sustain. @@ -160,7 +160,7 @@ e.plot; // 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,e.bufnum,2,fftSize:2048)[0]), //reading the channel of the activations on the pick dictionary + LagUD.ar(K2A.ar(FluidNMFMatch.kr(source,e.bufnum,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 @@ -215,17 +215,17 @@ Routine { // copy at least one other rank to a third rank, a sort of left-over channel ( Routine{ - FluidBufCompose.process(s, x.bufnum, startChan:~dog, nChans: 1, dstBufNum: e.bufnum); - FluidBufCompose.process(s, x.bufnum, startChan:~bird, nChans: 1, dstStartChan: 1, dstBufNum: e.bufnum, dstGain:1); + FluidBufCompose.process(s, x.bufnum, startChan:~dog, numChans: 1, destination: e.bufnum); + FluidBufCompose.process(s, x.bufnum, startChan:~bird, numChans: 1, destStartChan: 1, destination: e.bufnum, destGain:1); s.sync; - (0..9).removeAll([~dog,~bird]).do({|chan|FluidBufCompose.process(s,x.bufnum, startChan:chan, nChans: 1, dstStartChan: 2, dstBufNum: e.bufnum, dstGain:1)}); + (0..9).removeAll([~dog,~bird]).do({|chan|FluidBufCompose.process(s,x.bufnum, startChan:chan, numChans: 1, destStartChan: 2, destination: e.bufnum, destGain:1)}); s.sync; e.query; }.play; ) e.plot; -//using this trained dictionary we can then see the activation... +//using this trained basis we can then see the activation... ( { var source, blips; @@ -260,7 +260,7 @@ e.plot; :: STRONG::Pretrained piano:: CODE:: -//load in the sound in and a pretrained dictionary +//load in the sound in and a pretrained basis ( b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav"); c = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/filters/piano-dicts.wav"); @@ -268,7 +268,7 @@ e.plot; b.play c.query -//use the pretrained dictionary to compute activations of each notes to drive the amplitude of a resynth +//use the pretrained bases to compute activations of each notes to drive the amplitude of a resynth ( { var source, resynth; diff --git a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp index 68c260f..d4d67b2 100644 --- a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp @@ -4,9 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition DESCRIPTION:: -This class implements many spectral based onset detection algorythms, most of them taken from the literature (http://www.dafx.ca/proceedings/papers/p_133.pdf) - -It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: +This class implements many spectral based onset detection functions, 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 Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. @@ -20,28 +18,31 @@ ARGUMENT:: in The audio to be processed. ARGUMENT:: function - 0 - Energy =sum of squares of magnitudes / nBins (\power) - 1 - HFC = sum of (squared mag * binNum) / nBins - 2 - SpectralFlux = dif in mag between consecutive frames (half rectified) - 3 - MKL = sum of log of mag ratio per bin (or: sum of dif of log mag per bin) (\mkl) - 4 - IS = itakura - saito divergence - 5 - Cosine = cosine distance - 6 - PhaseDev = takes 2 past frames, project to current, as anticipated steady state, then compute differences (sums) (\phase) - 7 - WPhaseDev = same, but weighted by magnitude to remove chaos noise floor (\wphase) - 8 - ComplexDev = same as kPhaseDev, but in the complex domain - anticipated amp(steady) and phase(projected) - complex subtraction -> sum of mag (\complex) - 9 - RComplexDev =same as above, but rectified (\rcomplex) - -ARGUMENT:: thresh -diff for each... + The function 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 function, from 0 upwards. ARGUMENT:: debounce - The minimum duration of a slice in number of hops. + The minimum duration of a slice in number of hopSize. -ARGUMENT:: filtSize +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 -distance in samples between the comparison window (flux,mkl,kls,cosine) + For certain functions (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:: winSize 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 @@ -56,10 +57,29 @@ 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 winSize. + An audio stream with impulses at detected transients. The latency between the input and the output is winSize at maximum. EXAMPLES:: code:: -(some example code) +//load some sounds +b = Buffer.read(s,File.realpath(FluidOnsetSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); + +// basic param (the process add a latency of winSize samples +{var sig = PlayBuf.ar(1,b.bufnum,loop:1); [FluidOnsetSlice.ar(sig) * 0.5, DelayN.ar(sig, 1, 1024/ s.sampleRate)]}.play + +// other parameters +{var sig = PlayBuf.ar(1,b.bufnum,loop:1); [FluidOnsetSlice.ar(sig, 2, 0.06, 55, 7, 0, 128, 64) * 0.5, DelayN.ar(sig, 1, (128)/ s.sampleRate)]}.play + +// more musical trans-trigged autopan +( +{ + var sig, trig, syncd, pan; + sig = PlayBuf.ar(1,b.bufnum,loop:1); + trig = FluidOnsetSlice.ar(sig, 1, 1.8, 100, 8, 0, 128); + syncd = DelayN.ar(sig, 1, ( 128 / s.sampleRate)); + pan = TRand.ar(-1,1,trig); + Pan2.ar(syncd,pan); +}.play +) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidSines.schelp b/release-packaging/HelpSource/Classes/FluidSines.schelp index f7cfbd6..828ea1e 100644 --- a/release-packaging/HelpSource/Classes/FluidSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidSines.schelp @@ -21,10 +21,10 @@ METHOD:: ar ARGUMENT:: in The input to be processed -ARGUMENT:: bw +ARGUMENT:: bandwidth The width in bins of the fragment of the fft window that is considered a normal deviation for a potential continuous sinusoidal track. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. -ARGUMENT:: thresh +ARGUMENT:: threshold The normalised threshold, between 0 an 1, to consider a peak as a sinusoidal component from the normalized cross-correlation. ARGUMENT:: minTrackLen @@ -59,7 +59,7 @@ CODE:: b = Buffer.read(s,File.realpath(FluidSines.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav"); // run with large parameters - left is sinusoidal model, right is residual -{FluidSines.ar(PlayBuf.ar(1,b.bufnum,loop:1),thresh: 0.2, minTrackLen: 2, winSize: 2048, fftSize: 8192)}.play +{FluidSines.ar(PlayBuf.ar(1,b.bufnum,loop:1),threshold: 0.2, minTrackLen: 2, winSize: 2048, fftSize: 8192)}.play // interactive parameters with a narrower bandwidth {FluidSines.ar(PlayBuf.ar(1,b.bufnum,loop:1),30,MouseX.kr(), 5, winSize: 1000, hopSize: 200, fftSize: 4096)}.play diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp index 4e91923..779453c 100644 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp @@ -216,7 +216,7 @@ x = { s.freqscope -// at first it seems quite centred, but then flipped 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: +// 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) @@ -235,7 +235,7 @@ x = { }.play; ) -// this example shows a similar result to the brickwall bspectral bandpass above. If we move the central frequency nearer the half-Nyquist: +// 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) diff --git a/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc b/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc new file mode 100644 index 0000000..dfb6b1f --- /dev/null +++ b/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc @@ -0,0 +1,42 @@ +//this patch requests a folder and will iterate through all accepted audiofiles and concatenate them in the destination buffer. It will also yield an array with the numFrame where files start in the new buffer. + +( +var tempbuf,dest=0, fileNames; +c = [0]; + +FileDialog.new({|selection| + var total, totaldur = 0, maxchans = 0; + t = Main.elapsedTime; + fileNames = PathName.new(selection[0]) + .entries + .select({|f| + [\wav, \WAV, \mp3,\aif].includes(f.extension.asSymbol);}); + total = fileNames.size(); + fileNames.do({arg fp; + SoundFile.use(fp.asAbsolutePath , { + arg file; + var dur = file.numFrames; + c = c.add(dur); + totaldur = totaldur + dur; + maxchans = maxchans.max(file.numChannels); + }); + }); + Routine{ + b = Buffer.alloc(s,totaldur,maxchans); + s.sync; + fileNames.do{|f, i| + f.postln; + ("Loading"+(i+1)+"of"+total).postln; + tempbuf = Buffer.read(s, f.asAbsolutePath,action:{FluidBufCompose.process(s,tempbuf,destStartFrame:c[i],destination:b);}); + s.sync; + }; + ("loading buffers done in" + (Main.elapsedTime - t).round(0.1) + "seconds.").postln; + }.play; +}, fileMode:2); +) + +b.plot +c.postln +b.play + +Buffer.freeAll \ No newline at end of file diff --git a/release-packaging/ignore/Examples/buffer_compositing/fileiterator.sc b/release-packaging/ignore/Examples/buffer_compositing/fileiterator.sc index 89cf368..e8ef47f 100644 --- a/release-packaging/ignore/Examples/buffer_compositing/fileiterator.sc +++ b/release-packaging/ignore/Examples/buffer_compositing/fileiterator.sc @@ -1,6 +1,8 @@ //destination buffer -b = Buffer.alloc(s,1); +( +b = Buffer.new(); c = Array.new(); +) //this patch requests a folder and will iterate through all accepted audiofiles and concatenate them in the destination buffer. It will also yield an array with the numFrame where files start in the new buffer. @@ -9,25 +11,24 @@ var tempbuf,dest=0, fileNames; FileDialog.new({|selection| var total; + t = Main.elapsedTime; fileNames = PathName.new(selection[0]) .entries .select({|f| [\wav, \WAV, \mp3,\aif].includes(f.extension.asSymbol);}); - total = fileNames.size() - 1; + total = fileNames.size(); Routine{ fileNames.do{|f, i| f.postln; - ("Loading"+i+"of"+total).postln; + ("Loading"+(i+1)+"of"+total).postln; tempbuf = Buffer.read(s,f.asAbsolutePath); s.sync; c = c.add(dest); - FluidBufCompose.process(s,tempbuf.bufnum,dstStartAtA:dest,srcBufNumB:b.bufnum,dstBufNum:b.bufnum); - s.sync; - b.updateInfo(); + FluidBufCompose.process(s,tempbuf,destStartFrame:dest,destination:b); s.sync; dest = b.numFrames; }; - "load buffers done".postln; + ("loading buffers done in" + (Main.elapsedTime - t).round(0.1) + "seconds.").postln; }.play; }, fileMode:2); ) diff --git a/release-packaging/ignore/Examples/nmf/JiT-NMF.scd b/release-packaging/ignore/Examples/nmf/JiT-NMF.scd index bb137ba..bc81aa4 100644 --- a/release-packaging/ignore/Examples/nmf/JiT-NMF.scd +++ b/release-packaging/ignore/Examples/nmf/JiT-NMF.scd @@ -47,9 +47,9 @@ y = Synth(\becauseIcan,[\bufnum, b.bufnum, \nmfa, c.bufnum, \nmfb, d.bufnum, \in ( w = OSCFunc({ arg msg; if(msg[3]== 1, { - FluidBufNMF.process(s, b.bufnum, nFrames: 22500, dstBufNum: c.bufnum, rank: 3, fftSize: 1024, winSize: 512, hopSize: 256); + FluidBufNMF.process(s, b, numFrames: 22500, destination: c.bufnum, rank: 3, fftSize: 1024, winSize: 512, hopSize: 256); }, { - FluidBufNMF.process(s, b.bufnum, 22050, 22500, dstBufNum: d.bufnum, rank: 3, fftSize: 1024, winSize: 512, hopSize: 256); + FluidBufNMF.process(s, b, 22050, 22500, destination: d.bufnum, rank: 3, fftSize: 1024, winSize: 512, hopSize: 256); });}, '/processplease', s.addr); )