From db960b09e2ee6faa0c8494e18e3862eeeb943628 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 22 Apr 2019 00:57:34 +0100 Subject: [PATCH 01/50] ssssh: Surpress a lot of unused argument and integer conversion warnings include/FluidSCWrapper.hpp --- include/FluidSCWrapper.hpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 3b9c92f..7fdec32 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -64,7 +64,7 @@ public: ft->fDefineUnitCmd(name, "latency", doLatency); } - static void doLatency(Unit *unit, sc_msg_iter *args) + static void doLatency(Unit *unit, sc_msg_iter*) { float l[]{static_cast(static_cast(unit)->mClient.latency())}; auto ft = Wrapper::getInterfaceTable(); @@ -102,19 +102,19 @@ public: mAudioInputs.reserve(mClient.audioChannelsIn()); mOutputs.reserve(std::max(mClient.audioChannelsOut(), mClient.controlChannelsOut())); - for (int i = 0; i < mClient.audioChannelsIn(); ++i) + for (size_t i = 0; i < mClient.audioChannelsIn(); ++i) { mInputConnections.emplace_back(isAudioRateIn(i)); mAudioInputs.emplace_back(nullptr, 0, 0); } - for (int i = 0; i < mClient.audioChannelsOut(); ++i) + for (size_t i = 0; i < mClient.audioChannelsOut(); ++i) { mOutputConnections.emplace_back(true); mOutputs.emplace_back(nullptr, 0, 0); } - for (int i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } + for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } set_calc_function(); Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); @@ -123,21 +123,21 @@ public: } - void next(int n) + void next(int) { mControlsIterator.reset(mInBuf + 1); //mClient.audioChannelsIn()); Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator); // forward on inputs N + audio inputs as params mParams.template constrainParameterValues(); const Unit *unit = this; - for (int i = 0; i < mClient.audioChannelsIn(); ++i) + for (size_t i = 0; i < mClient.audioChannelsIn(); ++i) { if (mInputConnections[i]) mAudioInputs[i].reset(IN(i), 0, fullBufferSize()); } - for (int i = 0; i < mClient.audioChannelsOut(); ++i) + for (size_t i = 0; i < mClient.audioChannelsOut(); ++i) { if (mOutputConnections[i]) mOutputs[i].reset(out(i), 0, fullBufferSize()); } - for (int i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(i), 0, 1); } + for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(i), 0, 1); } mClient.process(mAudioInputs, mOutputs); } @@ -165,14 +165,14 @@ class NonRealTime public: static void setup(InterfaceTable *ft, const char *name) { DefinePlugInCmd(name, launch, nullptr); } - NonRealTime(World *world, sc_msg_iter *args) + NonRealTime(World*, sc_msg_iter*) : mParams{Client::getParameterDescriptors()} , mClient{mParams} {} void init(){}; - static void launch(World *world, void *inUserData, struct sc_msg_iter *args, void *replyAddr) + static void launch(World *world, void */*inUserData*/, struct sc_msg_iter *args, void *replyAddr) { if (args->tags && ((std::string{args->tags}.size() - 1) != Client::getParameterDescriptors().count())) @@ -208,7 +208,7 @@ public: static bool process(World *world, void *data) { return static_cast(data)->process(world); } static bool exchangeBuffers(World *world, void *data) { return static_cast(data)->exchangeBuffers(world); } static bool tidyUp(World *world, void *data) { return static_cast(data)->tidyUp(world); } - static void destroy(World *world, void *data) { delete static_cast(data); } + static void destroy(World *, void *data) { delete static_cast(data); } protected: ParamSetType mParams; @@ -226,7 +226,7 @@ private: return {}; } - bool process(World *world) + bool process(World *) { Result r = mClient.process(); @@ -245,7 +245,7 @@ private: return true; } - bool tidyUp(World *world) + bool tidyUp(World *) { mParams.template forEachParamType(); return true; @@ -329,10 +329,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { static constexpr size_t argSize = C::getParameterDescriptors().template get().fixedSize; - auto fromArgs(World *w, FloatControlsIter& args, LongT::type, int) { return args.next(); } - auto fromArgs(World *w, FloatControlsIter& args, FloatT::type, int) { return args.next(); } - auto fromArgs(World *w, sc_msg_iter* args, LongT::type, int defVal) { return args->geti(defVal); } - auto fromArgs(World *w, sc_msg_iter* args, FloatT::type, int) { return args->getf(); } + auto fromArgs(World *, FloatControlsIter& args, LongT::type, int) { return args.next(); } + auto fromArgs(World *, FloatControlsIter& args, FloatT::type, int) { return args.next(); } + auto fromArgs(World *, sc_msg_iter* args, LongT::type, int defVal) { return args->geti(defVal); } + auto fromArgs(World *, sc_msg_iter* args, FloatT::type, int) { return args->getf(); } auto fromArgs(World *w, ArgType args, BufferT::type, int) { @@ -344,7 +344,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { ParamLiteralConvertor a; - for (auto i = 0; i < argSize; i++) + for (size_t i = 0; i < argSize; i++) a[i] = fromArgs(w, args, a[0], 0); return a.value(); From e6aa277ebfe4a447b3978dc57872f821018d17ad Mon Sep 17 00:00:00 2001 From: Gerard Date: Wed, 24 Apr 2019 16:20:43 +0100 Subject: [PATCH 02/50] adding MFCC --- src/FluidBufMFCC/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidBufMFCC/FluidBufMFCC.cpp | 13 +++++++++++++ src/FluidMFCC/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidMFCC/FluidMFCC.cpp | 13 +++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 src/FluidBufMFCC/CMakeLists.txt create mode 100644 src/FluidBufMFCC/FluidBufMFCC.cpp create mode 100644 src/FluidMFCC/CMakeLists.txt create mode 100644 src/FluidMFCC/FluidMFCC.cpp diff --git a/src/FluidBufMFCC/CMakeLists.txt b/src/FluidBufMFCC/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidBufMFCC/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufMFCC/FluidBufMFCC.cpp b/src/FluidBufMFCC/FluidBufMFCC.cpp new file mode 100644 index 0000000..d5147ff --- /dev/null +++ b/src/FluidBufMFCC/FluidBufMFCC.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidiBufMFCC", ft); +} diff --git a/src/FluidMFCC/CMakeLists.txt b/src/FluidMFCC/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidMFCC/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidMFCC/FluidMFCC.cpp b/src/FluidMFCC/FluidMFCC.cpp new file mode 100644 index 0000000..8e786ff --- /dev/null +++ b/src/FluidMFCC/FluidMFCC.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidMFCC", ft); +} From 578d87ee3f14f60b576ef1d480bd74d99a7a9ded Mon Sep 17 00:00:00 2001 From: Gerard Date: Wed, 24 Apr 2019 16:21:03 +0100 Subject: [PATCH 03/50] adding Pitch --- src/FluidBufPitch/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidBufPitch/FluidBufPitch.cpp | 13 +++++++++++++ src/FluidPitch/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidPitch/FluidPitch.cpp | 13 +++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 src/FluidBufPitch/CMakeLists.txt create mode 100644 src/FluidBufPitch/FluidBufPitch.cpp create mode 100644 src/FluidPitch/CMakeLists.txt create mode 100644 src/FluidPitch/FluidPitch.cpp diff --git a/src/FluidBufPitch/CMakeLists.txt b/src/FluidBufPitch/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidBufPitch/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufPitch/FluidBufPitch.cpp b/src/FluidBufPitch/FluidBufPitch.cpp new file mode 100644 index 0000000..12553a5 --- /dev/null +++ b/src/FluidBufPitch/FluidBufPitch.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidBufPitch", ft); +} diff --git a/src/FluidPitch/CMakeLists.txt b/src/FluidPitch/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidPitch/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidPitch/FluidPitch.cpp b/src/FluidPitch/FluidPitch.cpp new file mode 100644 index 0000000..4821cc4 --- /dev/null +++ b/src/FluidPitch/FluidPitch.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidPitch", ft); +} From 5b723b2b984ff6a7ac58bcb9a14199e281356cf7 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 24 Apr 2019 20:52:41 +0100 Subject: [PATCH 04/50] helpfile for bufspectralshape --- .../Classes/FluidBufSpectralShape.schelp | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp new file mode 100644 index 0000000..82897c6 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -0,0 +1,120 @@ +TITLE:: FluidBufSpectralShape +SUMMARY:: Seven Spectral Shape Descriptors on a Buffer +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile + + +DESCRIPTION:: +This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: +LIST:: +##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: + LIST:: + ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. + ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. + ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. + ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. + :: + ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. + ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. + ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. + + The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. +:: + + The process will return a multichannel buffer with the seven channels per input channel, each containing the 7 shapes. Each sample represents a value, which is every hopSize. + +CLASSMETHODS:: + +METHOD:: process + This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. + +ARGUMENT:: server + The server on which the buffers to be processed are allocated. + +ARGUMENT:: source + The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially. + +ARGUMENT:: startFrame + Where in the srcBuf should the process start, in sample. + +ARGUMENT:: numFrames + How many frames should be processed. + +ARGUMENT:: startChan + For multichannel srcBuf, which channel should be processed first. + +ARGUMENT:: numChans + For multichannel srcBuf, how many channel should be processed. + +ARGUMENT:: features + The destination buffer for the 7 spectral features describing the spectral shape. + +ARGUMENT:: 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 + +ARGUMENT:: hopSize + The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + +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 [features] as an argument. + +RETURNS:: + Nothing, as the various destination buffers are declared in the function call. + +EXAMPLES:: + +code:: +// create some buffers +( +b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav"); +c = Buffer.new(s); +) + +// run the process with basic parameters +( +Routine{ + t = Main.elapsedTime; + FluidBufSpectralShape.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// listen to the source and look at the buffer +b.play; + c.plot(minval:-10, maxval:0) +:: + +STRONG::A stereo buffer example.:: +CODE:: + +// load two very different files +( +b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav"); +c = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +) + +// composite one on left one on right as test signals +FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) +b.play + +// create 2 new buffers as destinations +d = Buffer.new(s); e = Buffer.new(s); + +//run the process on them +( +Routine{ + t = Main.elapsedTime; + FluidBufSines.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +//listen: stereo preserved! +d.play +e.play +:: From 7cdc040b178a3379e0b1784957b701f300e62eae Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 25 Apr 2019 15:20:01 +0100 Subject: [PATCH 05/50] bufspectralshape help is now finished --- .../Classes/FluidBufSpectralShape.schelp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index 82897c6..1eace3a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -86,7 +86,7 @@ Routine{ // listen to the source and look at the buffer b.play; - c.plot(minval:-10, maxval:0) +c.plot(minval:-25, maxval:150) :: STRONG::A stereo buffer example.:: @@ -102,19 +102,18 @@ c = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirn FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) b.play -// create 2 new buffers as destinations -d = Buffer.new(s); e = Buffer.new(s); +// create a buffer as destinations +c = Buffer.new(s); //run the process on them ( Routine{ t = Main.elapsedTime; - FluidBufSines.process(s, b, features: c); + FluidBufSpectralShape.process(s, b, features: c); (Main.elapsedTime - t).postln; }.play ) -//listen: stereo preserved! -d.play -e.play -:: +// look at the buffer: 7shapes for left, then 7 shapes for right +c.plot(minval:-25, maxval:150) +:: \ No newline at end of file From 37751b630731c3198b350c1a89db9b5f4c69e660 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 25 Apr 2019 16:51:54 +0100 Subject: [PATCH 06/50] bufPitch and bufMFCC correct name to the table --- src/FluidBufMFCC/FluidBufMFCC.cpp | 2 +- src/FluidBufPitch/FluidBufPitch.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FluidBufMFCC/FluidBufMFCC.cpp b/src/FluidBufMFCC/FluidBufMFCC.cpp index d5147ff..d4a9912 100644 --- a/src/FluidBufMFCC/FluidBufMFCC.cpp +++ b/src/FluidBufMFCC/FluidBufMFCC.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidiBufMFCC", ft); + makeSCWrapper("BufMFCC", ft); } diff --git a/src/FluidBufPitch/FluidBufPitch.cpp b/src/FluidBufPitch/FluidBufPitch.cpp index 12553a5..b4e5f8b 100644 --- a/src/FluidBufPitch/FluidBufPitch.cpp +++ b/src/FluidBufPitch/FluidBufPitch.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidBufPitch", ft); + makeSCWrapper("BufPitch", ft); } From f0f71a479a7d304e2bfee46b496cfbb5682571e4 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 25 Apr 2019 20:20:22 +0100 Subject: [PATCH 07/50] Build HISSFFT locally as static lib, just once rather than once per object. Also we don't see its warnings any more scripts/target_post.cmake CMakeLists.txt --- CMakeLists.txt | 11 +++++++ scripts/target_post.cmake | 63 +++++++++++++++------------------------ 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index caab9fd..b0db77e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,17 @@ if(APPLE OR WIN32) set(CMAKE_SHARED_MODULE_SUFFIX ".scx") endif() +get_property(FFT_SOURCES TARGET HISSTools_FFT PROPERTY INTERFACE_SOURCES) +get_property(FFT_LINK TARGET HISSTools_FFT PROPERTY INTERFACE_LINK_LIBRARIES) + +add_library(FFTLIB STATIC ${FFT_SOURCES}) +target_link_libraries( + FFTLIB PRIVATE ${FFT_LINK} +) +target_compile_options( + FFTLIB PRIVATE "$<$>: -mavx -msse -msse2 -msse3 -msse4>" +) + add_library(FLUID_SC_WRAPPER INTERFACE) target_sources(FLUID_SC_WRAPPER INTERFACE diff --git a/scripts/target_post.cmake b/scripts/target_post.cmake index 8b898e8..efa7fa3 100644 --- a/scripts/target_post.cmake +++ b/scripts/target_post.cmake @@ -5,17 +5,30 @@ else() target_compile_options(${PLUGIN} PRIVATE -Wall -Wextra -Wpedantic -Wreturn-type -Wconversion) endif() +set_target_properties(${PLUGIN} PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO +) + target_link_libraries( ${PLUGIN} - PRIVATE + PUBLIC FLUID_DECOMPOSITION FLUID_SC_WRAPPER + PRIVATE + FFTLIB ) + target_include_directories( ${PLUGIN} PRIVATE ${LOCAL_INCLUDES} +) + +target_include_directories( + ${PLUGIN} SYSTEM PRIVATE ${SC_PATH}/include/plugin_interface ${SC_PATH}/include/common @@ -42,45 +55,17 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) include (CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG(-msse HAS_CXX_SSE) - CHECK_CXX_COMPILER_FLAG(-msse2 HAS_CXX_SSE2) - CHECK_CXX_COMPILER_FLAG(-mfpmath=sse HAS_CXX_FPMATH_SSE) - CHECK_CXX_COMPILER_FLAG(-mavx HAS_AVX) - CHECK_CXX_COMPILER_FLAG(-mavx2 HAS_AVX2) - # target_compile_features( - # ${PLUGIN} - # PUBLIC - # "$<$>: -mavx -msse -msse2 -msse3 -msse4>" - # - # ) - - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse") - # endif() - # - # CHECK_C_COMPILER_FLAG(-msse2 HAS_SSE2) - # - # - # if (HAS_SSE2) - # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2") - # endif() - # if (HAS_CXX_SSE2) - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2") - # endif() - # - # - # - # if (HAS_FPMATH_SSE) - # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse") - # endif() - # if (HAS_CXX_FPMATH_SSE) - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse") - # endif() - # - # if(NATIVE) - # add_definitions(-march=native) - # endif() - # + # CHECK_CXX_COMPILER_FLAG(-msse HAS_CXX_SSE) + # CHECK_CXX_COMPILER_FLAG(-msse2 HAS_CXX_SSE2) + # CHECK_CXX_COMPILER_FLAG(-mfpmath=sse HAS_CXX_FPMATH_SSE) + # CHECK_CXX_COMPILER_FLAG(-mavx HAS_AVX) + # CHECK_CXX_COMPILER_FLAG(-mavx2 HAS_AVX2) + target_compile_features( + ${PLUGIN} + PUBLIC + "$<$>: -mavx -msse -msse2 -msse3 -msse4>" + ) endif() if(MINGW) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mstackrealign") From d407f97f37e4325939447796d5584acfb12965f7 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 25 Apr 2019 20:20:55 +0100 Subject: [PATCH 08/50] ssssh: deal with warnings about integers and unused parameters --- include/FluidSCWrapper.hpp | 19 ++++++++++--------- include/SCBufferAdaptor.hpp | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 7fdec32..095b449 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -76,7 +76,7 @@ public: } RealTime() - : mControlsIterator{mInBuf + mSpecialIndex + 1,mNumInputs - mSpecialIndex - 1} + : mControlsIterator{mInBuf + mSpecialIndex + 1,static_cast(static_cast(mNumInputs) - mSpecialIndex - 1)} , mParams{Wrapper::Client::getParameterDescriptors()} , mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)} {} @@ -102,19 +102,19 @@ public: mAudioInputs.reserve(mClient.audioChannelsIn()); mOutputs.reserve(std::max(mClient.audioChannelsOut(), mClient.controlChannelsOut())); - for (size_t i = 0; i < mClient.audioChannelsIn(); ++i) + for (int i = 0; i < static_cast(mClient.audioChannelsIn()); ++i) { mInputConnections.emplace_back(isAudioRateIn(i)); mAudioInputs.emplace_back(nullptr, 0, 0); } - for (size_t i = 0; i < mClient.audioChannelsOut(); ++i) + for (int i = 0; i < static_cast(mClient.audioChannelsOut()); ++i) { mOutputConnections.emplace_back(true); mOutputs.emplace_back(nullptr, 0, 0); } - for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } + for (int i = 0; i < static_cast(mClient.controlChannelsOut()); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } set_calc_function(); Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); @@ -135,9 +135,9 @@ public: } for (size_t i = 0; i < mClient.audioChannelsOut(); ++i) { - if (mOutputConnections[i]) mOutputs[i].reset(out(i), 0, fullBufferSize()); + if (mOutputConnections[i]) mOutputs[i].reset(out(static_cast(i)), 0, fullBufferSize()); } - for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(i), 0, 1); } + for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(static_cast(i)), 0, 1); } mClient.process(mAudioInputs, mOutputs); } @@ -202,7 +202,7 @@ public: if (msgSize) { args->getb(completionMessage.data(), msgSize); } world->ft->fDoAsynchronousCommand(world, replyAddr, Wrapper::getName(), w, process, exchangeBuffers, tidyUp, destroy, - msgSize, completionMessage.data()); + static_cast(msgSize), completionMessage.data()); } static bool process(World *world, void *data) { return static_cast(data)->process(world); } @@ -336,16 +336,17 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase auto fromArgs(World *w, ArgType args, BufferT::type, int) { - typename LongT::type bufnum = fromArgs(w, args, LongT::type(), -1); + typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } typename T::type operator()(World *w, ArgType args) { ParamLiteralConvertor a; + using LiteralType = typename ParamLiteralConvertor::LiteralType; for (size_t i = 0; i < argSize; i++) - a[i] = fromArgs(w, args, a[0], 0); + a[i] = static_cast(fromArgs(w, args, a[0], 0)); return a.value(); } diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index cff93bc..2be53ec 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -25,11 +25,11 @@ struct NRTBuf { : mBuffer(b) { } - NRTBuf(World *world, long bufnum, bool rt = false) + NRTBuf(World *world, uint32 bufnum, bool rt = false) : NRTBuf(rt ? World_GetBuf(world, bufnum) : World_GetNRTBuf(world, bufnum)) { - if (mBuffer && !mBuffer->samplerate) + if (mBuffer && !static_cast(mBuffer->samplerate)) mBuffer->samplerate = world->mFullRate.mSampleRate; } @@ -64,8 +64,8 @@ public: SCBufferAdaptor& operator=(SCBufferAdaptor&&) = default; - SCBufferAdaptor(long bufnum,World *world, bool rt = false) - : NRTBuf(world, bufnum, rt) + SCBufferAdaptor(intptr_t bufnum,World *world, bool rt = false) + : NRTBuf(world, static_cast(bufnum), rt) , mBufnum(bufnum) , mWorld(world) { @@ -78,7 +78,7 @@ public: void assignToRT(World *rtWorld) { - SndBuf *rtBuf = World_GetBuf(rtWorld, mBufnum); + SndBuf *rtBuf = World_GetBuf(rtWorld, static_cast(mBufnum)); *rtBuf = *mBuffer; rtWorld->mSndBufUpdates[mBufnum].writes++; } @@ -130,15 +130,15 @@ public: size_t numFrames() const override { - return valid() ? this->mBuffer->frames : 0; + return valid() ? static_cast(this->mBuffer->frames) : 0u; } size_t numChans() const override { - return valid() ? this->mBuffer->channels / mRank : 0; + return valid() ? static_cast(this->mBuffer->channels) / mRank : 0u; } - size_t rank() const override { return valid() ? mRank : 0; } + size_t rank() const override { return valid() ? mRank : 0u; } double sampleRate() const override { return valid() ? mBuffer->samplerate : 0; } @@ -147,17 +147,17 @@ public: SndBuf *thisThing = mBuffer; mOldData = thisThing->data; mRank = rank; - mWorld->ft->fBufAlloc(mBuffer, channels * rank, frames, sampleRate); + mWorld->ft->fBufAlloc(mBuffer, static_cast(channels * rank), static_cast(frames), sampleRate); } - int bufnum() { return mBufnum; } + intptr_t bufnum() { return mBufnum; } void realTime(bool rt) { mRealTime = rt; } protected: bool mRealTime{false}; float *mOldData{0}; - long mBufnum; + intptr_t mBufnum; World *mWorld; size_t mRank{1}; }; From 5a6e7bab6cd3077ffa46e3fe95aa714d31ce689d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 26 Apr 2019 09:11:28 +0100 Subject: [PATCH 09/50] Unbreak release builds, but I don't understand why --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b0db77e..266d91a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,9 +68,9 @@ add_library(FFTLIB STATIC ${FFT_SOURCES}) target_link_libraries( FFTLIB PRIVATE ${FFT_LINK} ) -target_compile_options( - FFTLIB PRIVATE "$<$>: -mavx -msse -msse2 -msse3 -msse4>" -) +# target_compile_features( +# FFTLIB PRIVATE "$<$>: -mavx -msse -msse2 -msse3 -msse4>" +# ) add_library(FLUID_SC_WRAPPER INTERFACE) target_sources(FLUID_SC_WRAPPER From 4b015780fefbe958c1862d707005df66b8fa7b14 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 26 Apr 2019 16:13:24 +0100 Subject: [PATCH 10/50] start of nmffFilter doc and def --- release-packaging/Classes/FluidNMFFilter.sc | 21 ++ .../HelpSource/Classes/FluidBufNMF.schelp | 2 +- .../HelpSource/Classes/FluidNMFFilter.schelp | 309 ++++++++++++++++++ .../HelpSource/Classes/FluidNMFMatch.schelp | 2 +- 4 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 release-packaging/Classes/FluidNMFFilter.sc create mode 100644 release-packaging/HelpSource/Classes/FluidNMFFilter.schelp diff --git a/release-packaging/Classes/FluidNMFFilter.sc b/release-packaging/Classes/FluidNMFFilter.sc new file mode 100644 index 0000000..757e8b2 --- /dev/null +++ b/release-packaging/Classes/FluidNMFFilter.sc @@ -0,0 +1,21 @@ +FluidNMFFilter : MultiOutUGen { + + *ar { arg in = 0, bases, maxRank = 1, numIter = 10, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('audio', in.asAudioRateInput(this), bases, maxRank, numIter, winSize, hopSize, fftSize, maxFFTSize); + } + + init {arg ...theInputs; + inputs = theInputs; + ^this.initOutputs(inputs[2],rate); + } + + checkInputs { + 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/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index dd67306..06b191e 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -1,7 +1,7 @@ TITLE:: FluidBufNMF SUMMARY:: Buffer-Based Non-Negative Matrix Factorisation on Spectral Frames CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidNMFMatch +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidNMFMatch, Classes/FluidNMFFilter DESCRIPTION:: diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp new file mode 100644 index 0000000..5e51771 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -0,0 +1,309 @@ +TITLE:: FluidNMFFilter +SUMMARY:: Real-Time Non-Negative Matrix Factorisation with Fixed Bases +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufNMF, Classes/FluidNMFMatch + +DESCRIPTION:: +The FluidNMFFilter object matches an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: + +It outputs at AR the resynthesis of the best factorisation. The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxRank:: parameter. + +NMF has been a popular technique in signal processing research for things like source separation and transcription footnote:: Smaragdis and Brown, Non-Negative Matrix Factorization for Polyphonic Music Transcription.::, although its creative potential is so far relatively unexplored. It works iteratively, by trying to find a combination of amplitudes ('activations') that yield the original magnitude spectrogram of the audio input when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results. + +The whole process can be related to a channel vocoder where, instead of fixed bandpass filters, we get more complex filter shapes and the activations correspond to channel envelopes. + +More information on possible musicianly uses of NMF are availabe in LINK::Guides/FluCoMa:: overview file. + +FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899). :: + + +CLASSMETHODS:: + +METHOD:: ar +The real-time processing method. It takes an audio input, and will yield a audio stream in the form of a multichannel array of size STRONG::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:: 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:: 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 + The number of samples that are analysed at a time. A lower number yields greater temporal resolution, at the expense of spectral resoultion, and vice-versa. + +ARGUMENT:: hopSize + The window hope size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + +ARGUMENT:: fftSize + The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. + +ARGUMENT:: maxFFTSize + How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. + +RETURNS:: + A multichannel kr output, giving for each basis component the activation amount. + + +EXAMPLES:: +STRONG::A didactic example:: +CODE:: +( +// create buffers +b= Buffer.alloc(s,44100); +c = Buffer.alloc(s, 44100); +d = Buffer.new(s); +e= Buffer.new(s); +) + +( +// fill them with 2 clearly segregated sine waves and composite a buffer where they are consecutive +Routine { + b.sine2([500],[1], false, false); + c.sine2([5000],[1],false, false); + s.sync; + FluidBufCompose.process(s,b, destination:d); + FluidBufCompose.process(s,c, destStartFrame:44100, destination:d, destGain:1); + s.sync; + d.query; +}.play; +) + +// check +d.plot +d.play //////(beware !!!! loud!!!) + +( +// separate them in 2 ranks +Routine { + FluidBufNMF.process(s, d, bases: e, rank:2); + s.sync; + e.query; +}.play +) + +// check for 2 spikes in the spectra +e.plot + +// +{FluidNMFFilter.ar(SinOsc.ar(500),e,2)}.play + +{FluidNMFFilter.ar(SinOsc.ar(5000),e,2)}.play + +{FluidNMFFilter.ar(SinOsc.ar([500,5000]).sum,e,2)}.play +:: + +STRONG::A pick compressor:: +CODE:: +//set some buffers +( +b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +c = Buffer.new(s); +x = Buffer.new(s); +e = Buffer.new(s); +) + +// train only 2 seconds +( +Routine { + FluidBufNMF.process(s,b,0,88200,0,1, c, x, rank:10,fftSize:2048); + c.query; +}.play; +) + +// wait for the query to print +// then find the rank that has the picking sound by changing which channel to listen to +( + ~element = 6; + {PlayBuf.ar(10,c)[~element]}.play; +) + +// 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, startChan: z.removeAt(~element), numChans: 1, destination: e); + z.do({|chan| FluidBufCompose.process(s, x, startChan:chan, numChans: 1, destStartChan: 1, destination: e, destGain:1)}); + e.query; +}.play; +) + +e.plot; + +//using this trained basis we can see the envelop (activations) of each rank +{FluidNMFMatch.kr(PlayBuf.ar(1,b),e,2,fftSize:2048)}.plot(1); +// the left/top activations are before, the pick before the sustain. + +//we can then use the activation value to sidechain a compression patch that is sent in a delay +( +{ + var source, todelay, delay1, delay2, delay3, feedback, mod1, mod2, mod3, mod4; + //read the source + source = PlayBuf.ar(1, b); + + // generate modulators that are coprime in frequency + mod1 = SinOsc.ar(1, 0, 0.001); + mod2 = SinOsc.ar(((617 * 181) / (461 * 991)), 0, 0.001); + mod3 = SinOsc.ar(((607 * 193) / (491 * 701)), 0, 0.001); + mod4 = SinOsc.ar(((613 * 191) / (463 * 601)), 0, 0.001); + + // compress the signal to send to the delays + todelay = DelayN.ar(source,0.1, 800/44100, //delaying it to compensate for FluidNMFMatch's latency + LagUD.ar(K2A.ar(FluidNMFMatch.kr(source,e,2,fftSize:2048)[0]), //reading the channel of the activations on the pick basis + 80/44100, // lag uptime (compressor's attack) + 1000/44100, // lag downtime (compressor's decay) + (1/(2.dbamp) // compressor's threshold inverted + )).clip(1,1000).pow((8.reciprocal)-1)); //clipping it so we only affect above threshold, then ratio(8) becomes the exponent of that base + + // delay network + feedback = LocalIn.ar(3);// take the feedback in for the delays + delay1 = DelayC.ar(BPF.ar(todelay+feedback[1]+(feedback[2] * 0.3), 987, 6.7,0.8),0.123,0.122+(mod1*mod2)); + delay2 = DelayC.ar(BPF.ar(todelay+feedback[0]+(feedback[2] * 0.3), 1987, 6.7,0.8),0.345,0.344+(mod3*mod4)); + delay3 = DelayC.ar(BPF.ar(todelay+feedback[1], 1456, 6.7,0.8),0.567,0.566+(mod1*mod3),0.6); + LocalOut.ar([delay1,delay2, delay3]); // write the feedback for the delays + + source.dup + ([delay1+delay3,delay2+delay3]*(-3.dbamp)) + //listen to the delays in solo by uncommenting the following line + // [delay1+delay3,delay2+delay3] +}.play; +) + +:: +STRONG::Object finder:: + CODE:: +//set some buffers +( +b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-BaB-SoundscapeGolcarWithDog.wav"); +c = Buffer.new(s); +x = Buffer.new(s); +e = Buffer.new(s); +) + +// train where all objects are present +( +Routine { + FluidBufNMF.process(s,b,130000,150000,0,1, c, x, rank:10); + c.query; +}.play; +) + +// wait for the query to print +// then find a rank for each item you want to find. You could also sum them. Try to find a rank with a good object-to-rest ratio +( + ~dog =2; + {PlayBuf.ar(10,c)[~dog]}.play +) + +( + ~bird = 3; + {PlayBuf.ar(10,c)[~bird]}.play +) + + +// copy at least one other rank to a third rank, a sort of left-over channel +( +Routine{ + FluidBufCompose.process(s, x, startChan:~dog, numChans: 1, destination: e); + FluidBufCompose.process(s, x, startChan:~bird, numChans: 1, destStartChan: 1, destination: e, destGain:1); + (0..9).removeAll([~dog,~bird]).do({|chan|FluidBufCompose.process(s,x, startChan:chan, numChans: 1, destStartChan: 2, destination: e, destGain:1)}); + e.query; +}.play; +) +e.plot; + +//using this trained basis we can then see the activation... (wait for 5 seconds before it prints!) +( +{ + var source, blips; + //read the source + source = PlayBuf.ar(2, b); + blips = FluidNMFMatch.kr(source.sum,e,3); + }.plot(5); +) + +// ...and use some threshold to 'find' objects... +( +{ + var source, blips; + //read the source + source = PlayBuf.ar(2, b); + blips = Schmidt.kr(FluidNMFMatch.kr(source.sum,e,3),0.5,[10,1,1000]); + }.plot(5); +) + +// ...and use these to sonify them +( +{ + var source, blips, dogs, birds; + //read the source + source = PlayBuf.ar(2, b); + blips = Schmidt.kr(FluidNMFMatch.kr(source.sum,e,3),0.5,[10,1,1000]); + dogs = SinOsc.ar(100,0,Lag.kr(blips[0],0.05,0.15)); + birds = SinOsc.ar(1000,0,Lag.kr(blips[1],0.05,0.05)); + [dogs, birds] + source; + }.play; +) +:: + STRONG::Pretrained piano:: + CODE:: +//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"); +) +b.play +c.query + +//use the pretrained bases to compute activations of each notes to drive the amplitude of a resynth +( +{ + var source, resynth; + source = PlayBuf.ar(2, b,loop:1).sum; + resynth = SinOsc.ar((21..108).midicps, 0, FluidNMFMatch.kr(source,c,88,10,4096).madd(0.002)).sum; + [source, resynth] +}.play +) + + +//now sample and hold the same stream to get notes identified, played and sent back via osc +( +{ + var source, resynth, chain, trig, acts; + source = PlayBuf.ar(2,b,loop:1).sum; + + // built in attack detection, delayed until the stable part of the sound + chain = FFT(LocalBuf(256), source); + trig = TDelay.kr(Onsets.kr(chain, 0.5),0.1); + + // samples and holds activation values that are scaled and capped, in effect thresholding them + acts = Latch.kr(FluidNMFMatch.kr(source,c,88,10,4096).linlin(15,20,0,0.1),trig); + + // resynths as in the previous example, with the values sent back to the language + resynth = SinOsc.ar((21..108).midicps, 0, acts).sum; + SendReply.kr(trig, '/activations', acts); + [source, resynth] + // [source, T2A.ar(trig)] + // resynth +}.play +) + +// define a receiver for the activations +( + OSCdef(\listener, {|msg| + var data = msg[3..]; + // removes the silent and spits out the indicies as midinote number + data.collect({arg item, i; if (item > 0.01, {i + 21})}).reject({arg item; item.isNil}).postln; + }, '/activations'); +) + +:: + STRONG::Strange Resonators:: + CODE:: + //to be completed +:: + \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp index 27bc90d..8067809 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp @@ -1,7 +1,7 @@ TITLE:: FluidNMFMatch SUMMARY:: Real-Time Non-Negative Matrix Factorisation with Fixed Bases CATEGORIES:: Libraries>FluidDecomposition -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufNMF +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufNMF, Classes/FluidNMFFilter DESCRIPTION:: The FluidNMFMatch object matches an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: From 35fe3a63c8b30864aa89537883556b6cf55befc3 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 26 Apr 2019 16:14:27 +0100 Subject: [PATCH 11/50] added the material to compile nmffilter --- src/FluidNMFFilter/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidNMFFilter/FluidNMFFilter.cpp | 14 ++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/FluidNMFFilter/CMakeLists.txt create mode 100644 src/FluidNMFFilter/FluidNMFFilter.cpp diff --git a/src/FluidNMFFilter/CMakeLists.txt b/src/FluidNMFFilter/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidNMFFilter/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidNMFFilter/FluidNMFFilter.cpp b/src/FluidNMFFilter/FluidNMFFilter.cpp new file mode 100644 index 0000000..6e20346 --- /dev/null +++ b/src/FluidNMFFilter/FluidNMFFilter.cpp @@ -0,0 +1,14 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidNMFFilter", ft); +} From 9678f5450bea619d23192b97cac49820cdb9305c Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 26 Apr 2019 16:15:16 +0100 Subject: [PATCH 12/50] Pitch class def and help (plus spectralshape polish) --- release-packaging/Classes/FluidPitch.sc | 18 +++ .../Classes/FluidBufSpectralShape.schelp | 4 +- .../HelpSource/Classes/FluidPitch.schelp | 108 ++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 release-packaging/Classes/FluidPitch.sc create mode 100644 release-packaging/HelpSource/Classes/FluidPitch.schelp diff --git a/release-packaging/Classes/FluidPitch.sc b/release-packaging/Classes/FluidPitch.sc new file mode 100644 index 0000000..d574407 --- /dev/null +++ b/release-packaging/Classes/FluidPitch.sc @@ -0,0 +1,18 @@ +FluidPitch : MultiOutUGen { + + *kr { arg in = 0, algorithm = 2, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), algorithm, winSize, hopSize, fftSize, maxFFTSize); + } + + init {arg ...theInputs; + inputs = theInputs; + ^this.initOutputs(2,rate); + } + + checkInputs { + if(inputs.at(5).rate != 'scalar') { + ^(": maxFFTSize cannot be modulated."); + }; + ^this.checkValidInputs; + } +} diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index 1eace3a..e43b67e 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -71,7 +71,7 @@ EXAMPLES:: code:: // create some buffers ( -b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav"); +b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); c = Buffer.new(s); ) @@ -86,7 +86,7 @@ Routine{ // listen to the source and look at the buffer b.play; -c.plot(minval:-25, maxval:150) +c.plot(minval:-5, maxval:250) :: STRONG::A stereo buffer example.:: diff --git a/release-packaging/HelpSource/Classes/FluidPitch.schelp b/release-packaging/HelpSource/Classes/FluidPitch.schelp new file mode 100644 index 0000000..8aa60e4 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidPitch.schelp @@ -0,0 +1,108 @@ +TITLE:: FluidPitch +SUMMARY:: A Selection of Pitch Descriptors in Real-Time +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Pitch + +DESCRIPTION:: +This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. 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 multichannel control steam with [pitch, confidence] values, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. + +CLASSMETHODS:: + +METHOD:: kr + The audio rate in, control rate out version of the object. + +ARGUMENT:: in + The audio to be processed. + +ARGUMENT:: algorithm + The algorithm to estimate the pitch. The options are: + TABLE:: + ## 0 || Cepstrum: TODO. + ## 1 || Harmonic Product Spectrum: TODO. + ## 2 || YinFFT: TODO. + :: + + +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 + +ARGUMENT:: hopSize + The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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:: maxFFTSize + How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. + +RETURNS:: + A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is winSize. + + +EXAMPLES:: + + +code:: +//create a monitoring bus for the descriptors +b = Bus.new(\control,0,4); + +//create a monitoring window for the values +( +w = Window("Frequency Monitor", Rect(10, 10, 220, 115)).front; + +c = Array.fill(4, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c[0].string = ("FluidPitch: "); +c[1].string = ("confidence: "); +c[2].string = ("SC Pitch: "); +c[3].string = ("Confidence: "); + +a = Array.fill(4, {arg i; + StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); +}); +) + +//routine to update the parameters +( +r = Routine { + { + + b.get({ arg val; + { + if(w.isClosed.not) { + val.do({arg item,index; + a[index].string = item.round(0.01)}) + } + }.defer + }); + + 0.1.wait; + }.loop + +}.play +) + +//test signals +( +x = { + arg freq=220, type = 0; + var source = Select.ar(type,[SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1)]); + Out.kr(b, FluidPitch.kr(source) ++ Pitch.kr(source)); + source.dup; +}.play; +) + +// comments on digital test signals being better in time domain and how real ones will collapse +x.set(\freq, 220) +x.set(\freq, 440) + +x.set(\freq, 11000) + +x.set(\type, 0) +:: + +STRONG::a more musical example:: +CODE:: +// to be translated from max (and compare to the vanilla one) +:: From c6f26c38e47483de528455fc7027518f0b51bed1 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 3 May 2019 08:20:26 -0500 Subject: [PATCH 13/50] updated nmffilter demo (with strange behaviour) --- .../HelpSource/Classes/FluidNMFFilter.schelp | 174 +++++------------- 1 file changed, 44 insertions(+), 130 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index 5e51771..8577132 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufNMF, Classes/FluidNMFMatch DESCRIPTION:: -The FluidNMFFilter object matches an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: +The FluidNMFFilter object decomposes and resynthesises an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: It outputs at AR the resynthesis of the best factorisation. The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxRank:: parameter. @@ -29,7 +29,7 @@ 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. + The maximum number of elements the NMF algorithm will try to divide the spectrogram of the source in. This dictates the number of output channels for the ugen. This cannot be modulated. ARGUMENT:: 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. @@ -90,7 +90,7 @@ Routine { // check for 2 spikes in the spectra e.plot -// +//listen how the filter isolates each component and places them in each channel separately. {FluidNMFFilter.ar(SinOsc.ar(500),e,2)}.play {FluidNMFFilter.ar(SinOsc.ar(5000),e,2)}.play @@ -98,7 +98,7 @@ e.plot {FluidNMFFilter.ar(SinOsc.ar([500,5000]).sum,e,2)}.play :: -STRONG::A pick compressor:: +STRONG::A guitar processor:: CODE:: //set some buffers ( @@ -117,9 +117,9 @@ Routine { ) // wait for the query to print -// then find the rank that has the picking sound by changing which channel to listen to +// then find the rank that has more sustain pitch than pick (TODO: use descriptors with stats) ( - ~element = 6; + ~element = 1; {PlayBuf.ar(10,c)[~element]}.play; ) @@ -135,11 +135,7 @@ Routine{ e.plot; -//using this trained basis we can see the envelop (activations) of each rank -{FluidNMFMatch.kr(PlayBuf.ar(1,b),e,2,fftSize:2048)}.plot(1); -// the left/top activations are before, the pick before the sustain. - -//we can then use the activation value to sidechain a compression patch that is sent in a delay +//we can then use the resynthesised signal to sent in a delay ( { var source, todelay, delay1, delay2, delay3, feedback, mod1, mod2, mod3, mod4; @@ -153,12 +149,7 @@ e.plot; mod4 = SinOsc.ar(((613 * 191) / (463 * 601)), 0, 0.001); // compress the signal to send to the delays - todelay = DelayN.ar(source,0.1, 800/44100, //delaying it to compensate for FluidNMFMatch's latency - LagUD.ar(K2A.ar(FluidNMFMatch.kr(source,e,2,fftSize:2048)[0]), //reading the channel of the activations on the pick basis - 80/44100, // lag uptime (compressor's attack) - 1000/44100, // lag downtime (compressor's decay) - (1/(2.dbamp) // compressor's threshold inverted - )).clip(1,1000).pow((8.reciprocal)-1)); //clipping it so we only affect above threshold, then ratio(8) becomes the exponent of that base + todelay = FluidNMFFilter.ar(source,e,2,fftSize:2048)[0]; //reading the channel of the activations on the pick basis // delay network feedback = LocalIn.ar(3);// take the feedback in for the delays @@ -167,143 +158,66 @@ e.plot; delay3 = DelayC.ar(BPF.ar(todelay+feedback[1], 1456, 6.7,0.8),0.567,0.566+(mod1*mod3),0.6); LocalOut.ar([delay1,delay2, delay3]); // write the feedback for the delays - source.dup + ([delay1+delay3,delay2+delay3]*(-3.dbamp)) + source.dup + ([delay1+delay3,delay2+delay3]*(3.dbamp)) //listen to the delays in solo by uncommenting the following line // [delay1+delay3,delay2+delay3] + // [source, todelay] }.play; ) :: -STRONG::Object finder:: +STRONG::Strange Processor:: CODE:: //set some buffers ( -b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-BaB-SoundscapeGolcarWithDog.wav"); -c = Buffer.new(s); -x = Buffer.new(s); -e = Buffer.new(s); +b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +c = Buffer.alloc(s,1025,3); +d = Buffer.alloc(s,44100); ) -// train where all objects are present +// play a source and circular record the last second, continuously ( -Routine { - FluidBufNMF.process(s,b,130000,150000,0,1, c, x, rank:10); - c.query; +e = { var source = PlayBuf.ar(1,b,loop:1); + BufWr.ar(source, d, Phasor.ar(1, end:44100)); + source.dup; }.play; ) -// wait for the query to print -// then find a rank for each item you want to find. You could also sum them. Try to find a rank with a good object-to-rest ratio -( - ~dog =2; - {PlayBuf.ar(10,c)[~dog]}.play -) - -( - ~bird = 3; - {PlayBuf.ar(10,c)[~bird]}.play -) - - -// copy at least one other rank to a third rank, a sort of left-over channel +// after at least 1 second, trigger a first factorisation ( -Routine{ - FluidBufCompose.process(s, x, startChan:~dog, numChans: 1, destination: e); - FluidBufCompose.process(s, x, startChan:~bird, numChans: 1, destStartChan: 1, destination: e, destGain:1); - (0..9).removeAll([~dog,~bird]).do({|chan|FluidBufCompose.process(s,x, startChan:chan, numChans: 1, destStartChan: 2, destination: e, destGain:1)}); - e.query; +Routine { + FluidBufNMF.process(s, d, bases:c, winSize:2048, rank:3); + c.query; }.play; ) -e.plot; - -//using this trained basis we can then see the activation... (wait for 5 seconds before it prints!) -( -{ - var source, blips; - //read the source - source = PlayBuf.ar(2, b); - blips = FluidNMFMatch.kr(source.sum,e,3); - }.plot(5); -) +c.plot -// ...and use some threshold to 'find' objects... -( -{ - var source, blips; - //read the source - source = PlayBuf.ar(2, b); - blips = Schmidt.kr(FluidNMFMatch.kr(source.sum,e,3),0.5,[10,1,1000]); - }.plot(5); -) - -// ...and use these to sonify them -( -{ - var source, blips, dogs, birds; - //read the source - source = PlayBuf.ar(2, b); - blips = Schmidt.kr(FluidNMFMatch.kr(source.sum,e,3),0.5,[10,1,1000]); - dogs = SinOsc.ar(100,0,Lag.kr(blips[0],0.05,0.15)); - birds = SinOsc.ar(1000,0,Lag.kr(blips[1],0.05,0.05)); - [dogs, birds] + source; - }.play; -) -:: - STRONG::Pretrained piano:: - CODE:: -//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"); -) -b.play -c.query - -//use the pretrained bases to compute activations of each notes to drive the amplitude of a resynth +// wait for the query to print +// then start the splitting effect ( -{ - var source, resynth; - source = PlayBuf.ar(2, b,loop:1).sum; - resynth = SinOsc.ar((21..108).midicps, 0, FluidNMFMatch.kr(source,c,88,10,4096).madd(0.002)).sum; - [source, resynth] -}.play +f = {var source = In.ar(0,2); + ReplaceOut.ar(0, Splay.ar(FluidNMFFilter.ar(source.sum, c, 3, winSize:2048))); +}.play(addAction:\addToTail); ) +// kill this boring splitter +f.free; -//now sample and hold the same stream to get notes identified, played and sent back via osc -( -{ - var source, resynth, chain, trig, acts; - source = PlayBuf.ar(2,b,loop:1).sum; - - // built in attack detection, delayed until the stable part of the sound - chain = FFT(LocalBuf(256), source); - trig = TDelay.kr(Onsets.kr(chain, 0.5),0.1); - - // samples and holds activation values that are scaled and capped, in effect thresholding them - acts = Latch.kr(FluidNMFMatch.kr(source,c,88,10,4096).linlin(15,20,0,0.1),trig); - - // resynths as in the previous example, with the values sent back to the language - resynth = SinOsc.ar((21..108).midicps, 0, acts).sum; - SendReply.kr(trig, '/activations', acts); - [source, resynth] - // [source, T2A.ar(trig)] - // resynth -}.play -) - -// define a receiver for the activations +// more fun: processing the 3 rank independently ( - OSCdef(\listener, {|msg| - var data = msg[3..]; - // removes the silent and spits out the indicies as midinote number - data.collect({arg item, i; if (item > 0.01, {i + 21})}).reject({arg item; item.isNil}).postln; - }, '/activations'); +f = {var source, x,y,z, rev, dist, bases = c.bufnum; + source = In.ar(0,2); + #x,y,z = FluidNMFFilter.ar(source.sum, bases, 3, winSize:2048); + rev = FreeVerb.ar(x); + dist = (z * 10).atan * 0.1; + ReplaceOut.ar(0, Splay.ar([rev,y,dist])); +}.play(addAction:\addToTail); ) +// here you can retrigger the factorisation +g = Buffer.alloc(s,1025,3); +FluidBufNMF.process(s, d, bases:g, winSize:2048, rank:3); +f.set(\bases, g.bufnum) +//free +f.free; e.free; :: - STRONG::Strange Resonators:: - CODE:: - //to be completed -:: - \ No newline at end of file From fe478d2f12b8216962b309cdc44753a15b838c4e Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 5 May 2019 16:54:33 +0100 Subject: [PATCH 14/50] almost finished pitch helpfile --- .../HelpSource/Classes/FluidPitch.schelp | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidPitch.schelp b/release-packaging/HelpSource/Classes/FluidPitch.schelp index 8aa60e4..257dda8 100644 --- a/release-packaging/HelpSource/Classes/FluidPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidPitch.schelp @@ -6,7 +6,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Pitch DESCRIPTION:: This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. 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 multichannel control steam with [pitch, confidence] values, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. + The process will return a multichannel control steam with [pitch, confidence] values, which will be repeated if no change happens within the algorithm, i.e. when the hopSize is larger than the server's kr period. CLASSMETHODS:: @@ -83,26 +83,72 @@ r = Routine { }.play ) -//test signals +//test signals, all in one synth ( x = { - arg freq=220, type = 0; - var source = Select.ar(type,[SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1)]); + arg freq=220, type = 0, noise = 0; + var source = PinkNoise.ar(noise) + Select.ar(type,[SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1), Mix.new(Array.fill(8, {arg i; SinOsc.ar(LFNoise1.kr(0.1.rand,10,220*(i+1)),mul:(i+1).reciprocal * 0.1)}))]); Out.kr(b, FluidPitch.kr(source) ++ Pitch.kr(source)); source.dup; }.play; ) -// comments on digital test signals being better in time domain and how real ones will collapse +// the built-in is slightly worse on pure sinewaves x.set(\freq, 220) x.set(\freq, 440) -x.set(\freq, 11000) +// adding harmonics, by changing to triangle (1), saw (2) or square (3) shows that spectral algo are more resilient when signal are richer +x.set(\type, 1) +x.set(\type, 2) +x.set(\type, 3) -x.set(\type, 0) +// adding noise shows the comparative sturdiness of the spectral pitch tracker +x.set(\noise, 0.05) :: STRONG::a more musical example:: CODE:: -// to be translated from max (and compare to the vanilla one) +// play a noisy synth file +b = Buffer.read(s,File.realpath(FluidPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"); +b.play(true); + +//insert a selective reverb - sending only the material with very high pitch confidence +( +f = {var source, rev; + source = In.ar(0,2); + rev = FreeVerb.ar(DelayN.ar(source,delaytime:1024/s.sampleRate) * Lag.kr((FluidPitch.kr(source.sum)[1] > 0.98),0.01), 1); + ReplaceOut.ar(0, rev+ source); +}.play(addAction:\addToTail); +) + +// free the effect +f.free + +// insert a stereo delay instead (as well) using the same +( +f = { + var source, todelay, delay1, delay2, delay3, feedback, mod1, mod2, mod3, mod4; + //read the source + source = In.ar(0,2); + + // generate modulators that are coprime in frequency + mod1 = SinOsc.ar(1, 0, 0.001); + mod2 = SinOsc.ar(((617 * 181) / (461 * 991)), 0, 0.001); + mod3 = SinOsc.ar(((607 * 193) / (491 * 701)), 0, 0.001); + mod4 = SinOsc.ar(((613 * 191) / (463 * 601)), 0, 0.001); + + // gate the signal to send to the delays + todelay = DelayN.ar(source,delaytime:1024/s.sampleRate) * Lag.kr((FluidPitch.kr(source.sum)[1] > 0.98),0.01); + + // delay network + feedback = LocalIn.ar(3);// take the feedback in for the delays + delay1 = DelayC.ar(BPF.ar(todelay+feedback[1]+(feedback[2] * 0.3), 987, 6.7,0.35),0.123,0.122+(mod1*mod2)); + delay2 = DelayC.ar(BPF.ar(todelay+feedback[0]+(feedback[2] * 0.3), 1987, 6.7,0.35),0.345,0.344+(mod3*mod4)); + delay3 = DelayC.ar(BPF.ar(todelay+feedback[1], 1456, 6.7,0.35),0.567,0.566+(mod1*mod3),0.6); + LocalOut.ar([delay1,delay2, delay3]); // write the feedback for the delays + + ReplaceOut.ar(0, source + [delay1+delay3,delay2+delay3]); +}.play(addAction:\addToTail); +) + :: From f0e80270bf02eccfc7fa1e9a7edc2f260c370f94 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 7 May 2019 11:01:20 +0100 Subject: [PATCH 15/50] CMakeLists.txt: FFTLib compiling with GNU stdlib probably Not Good. Fix compiler switches (broken by quotes, but whyyyyy? But also wrong CMake command) scripts/target_post.cmake: Use right CMake command for compiler goodness --- CMakeLists.txt | 8 ++++---- scripts/target_post.cmake | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 266d91a..206fee1 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.3) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 14) - +set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -mavx -msse -msse2 -msse3 -msse4") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++") SET(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") @@ -68,9 +68,9 @@ add_library(FFTLIB STATIC ${FFT_SOURCES}) target_link_libraries( FFTLIB PRIVATE ${FFT_LINK} ) -# target_compile_features( -# FFTLIB PRIVATE "$<$>: -mavx -msse -msse2 -msse3 -msse4>" -# ) + target_compile_options( + FFTLIB PRIVATE $<$>:-mavx -msse -msse2 -msse3 -msse4> + ) add_library(FLUID_SC_WRAPPER INTERFACE) target_sources(FLUID_SC_WRAPPER diff --git a/scripts/target_post.cmake b/scripts/target_post.cmake index efa7fa3..e0f3481 100644 --- a/scripts/target_post.cmake +++ b/scripts/target_post.cmake @@ -61,7 +61,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) # CHECK_CXX_COMPILER_FLAG(-mavx HAS_AVX) # CHECK_CXX_COMPILER_FLAG(-mavx2 HAS_AVX2) - target_compile_features( + target_compile_options( ${PLUGIN} PUBLIC "$<$>: -mavx -msse -msse2 -msse3 -msse4>" From 117ccd55f8ed57cc91790dd28fb4c3a98560b75a Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 7 May 2019 13:49:24 +0100 Subject: [PATCH 16/50] bufPitch class def and helpfile in progress --- release-packaging/Classes/FluidBufPitch.sc | 24 ++++ .../HelpSource/Classes/FluidBufPitch.schelp | 115 ++++++++++++++++++ .../Classes/FluidBufSpectralShape.schelp | 2 +- 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 release-packaging/Classes/FluidBufPitch.sc create mode 100644 release-packaging/HelpSource/Classes/FluidBufPitch.schelp diff --git a/release-packaging/Classes/FluidBufPitch.sc b/release-packaging/Classes/FluidBufPitch.sc new file mode 100644 index 0000000..5780e93 --- /dev/null +++ b/release-packaging/Classes/FluidBufPitch.sc @@ -0,0 +1,24 @@ +FluidBufPitch{ + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, winSize = 1024, hopSize = -1, fftSize = -1, action; + + var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw}; + features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw}; + + server = server ? Server.default; + + //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, \BufPitch, source, startFrame, numFrames, startChan, numChans, features, algorithm, winSize, hopSize, fftSize, maxFFTSize); + server.sync; + features = server.cachedBufferAt(features); features.updateInfo; server.sync; + action.value(features); + }; + } +} diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp new file mode 100644 index 0000000..88313f9 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -0,0 +1,115 @@ +TITLE:: FluidBufPitch +SUMMARY:: A Selection of Pitch Descriptors on a Buffer +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile + + +DESCRIPTION:: +This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. It is part of the 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 multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. Each sample represents a value, which is every hopSize. + +CLASSMETHODS:: + +METHOD:: process + This is the method that calls for the pitch descriptor to be calculated on a given source buffer. + +ARGUMENT:: server + The server on which the buffers to be processed are allocated. + +ARGUMENT:: source + The index of the buffer to use as the source material to be pitch-tracked. The different channels of multichannel buffers will be processing sequentially. + +ARGUMENT:: startFrame + Where in the srcBuf should the process start, in sample. + +ARGUMENT:: numFrames + How many frames should be processed. + +ARGUMENT:: startChan + For multichannel srcBuf, which channel should be processed first. + +ARGUMENT:: numChans + For multichannel srcBuf, how many channel should be processed. + +ARGUMENT:: features + The destination buffer for the pitch descriptor. + +ARGUMENT:: algorithm + The algorithm to estimate the pitch. The options are: + TABLE:: + ## 0 || Cepstrum: TODO. + ## 1 || Harmonic Product Spectrum: TODO. + ## 2 || YinFFT: TODO. + :: + +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 + +ARGUMENT:: hopSize + The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + +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 [features] as an argument. + +RETURNS:: + Nothing, as the destination buffer is declared in the function call. + +EXAMPLES:: + +code:: +// create some buffers +( +b = Buffer.read(s,File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"); +c = Buffer.new(s); +) + +// run the process with basic parameters +( +Routine{ + t = Main.elapsedTime; + FluidBufPitch.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// listen to the source and look at the buffer +b.play; +c.plot(minval:0, maxval:20000) +// plot with a different range to appreciate the confidence: +c.plot(minval:0, maxval:1) +// interleaved [pitch,confidence] values in the buffer +c.getn(0,100,{|x|x.postln}) +:: + +STRONG::A stereo buffer example.:: +CODE:: + +// load two very different files +( +b = Buffer.read(s,File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav"); +c = Buffer.read(s,File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +) + +// composite one on left one on right as test signals +FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) +b.play + +// create a buffer as destinations +c = Buffer.new(s); + +//run the process on them +( +Routine{ + t = Main.elapsedTime; + FluidBufPitch.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// look at the buffer: [pitch,confidence] for left then [pitch,confidence] for right +c.plot(minval:0, maxval:1500) +:: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index e43b67e..6e142e3 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -64,7 +64,7 @@ ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. RETURNS:: - Nothing, as the various destination buffers are declared in the function call. + Nothing, as the destination buffer is declared in the function call. EXAMPLES:: From 7f5bd09321e82ce4db721a36185f784b380ddbe1 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 8 May 2019 11:11:31 +0100 Subject: [PATCH 17/50] help pitch and buf pitch finished (plus typo in nmffilter) --- .../HelpSource/Classes/FluidBufPitch.schelp | 88 ++++++++++++++++--- .../HelpSource/Classes/FluidNMFFilter.schelp | 3 +- .../HelpSource/Classes/FluidPitch.schelp | 9 +- 3 files changed, 81 insertions(+), 19 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 88313f9..06e9484 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -7,7 +7,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Class DESCRIPTION:: This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. 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 multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. Each sample represents a value, which is every hopSize. +The process will return a multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. Each sample represents a value, which is every hopSize. Its sampling rate is sourceSR / hopSize. CLASSMETHODS:: @@ -33,14 +33,14 @@ ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. ARGUMENT:: features - The destination buffer for the pitch descriptor. + The destination buffer for the pitch descriptors. ARGUMENT:: algorithm - The algorithm to estimate the pitch. The options are: + The algorithm to estimate the pitch. The options are: TABLE:: - ## 0 || Cepstrum: TODO. - ## 1 || Harmonic Product Spectrum: TODO. - ## 2 || YinFFT: TODO. + ## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC). + ## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 :: + ## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications.” QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html :: ARGUMENT:: winSize @@ -61,12 +61,15 @@ RETURNS:: EXAMPLES:: code:: -// create some buffers +// create a buffer with a short clicking sinusoidal burst (220Hz) starting at frame 8192 for 1024 frames ( -b = Buffer.read(s,File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"); +b = Buffer.sendCollection(s, (Array.fill(8192,{0}) ++ (Signal.sineFill(1203,[0,0,0,0,0,1],[0,0,0,0,0,0.5pi]).takeThese({|x,i|i>1023})) ++ Array.fill(8192,{0}))); c = Buffer.new(s); ) +// listen to the source and look at the buffer +b.play; b.plot; + // run the process with basic parameters ( Routine{ @@ -76,13 +79,16 @@ Routine{ }.play ) -// listen to the source and look at the buffer -b.play; -c.plot(minval:0, maxval:20000) +// look at the analysis +c.plot(minval:0, maxval:400) // plot with a different range to appreciate the confidence: c.plot(minval:0, maxval:1) -// interleaved [pitch,confidence] values in the buffer -c.getn(0,100,{|x|x.postln}) + +// The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopesize, then multiply by 2 because of the channel interleaving +// here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30. +c.getn(30,10,{|x|x.postln}) + +// observe that the first frame is silent, as expected. The next frame's confidence is low-ish, because the window is half full (window of 1024, overlap of 512). Then a full window is analysed, with strong confidence. Then another half full window, then silence, as expected. :: STRONG::A stereo buffer example.:: @@ -112,4 +118,60 @@ Routine{ // look at the buffer: [pitch,confidence] for left then [pitch,confidence] for right c.plot(minval:0, maxval:1500) +:: + +STRONG::A musical example.:: +code:: +// create some buffers +( +b = Buffer.read(s,File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"); +c = Buffer.new(s); +) + +// run the process with basic parameters and retrieve the array in the langage side +( +Routine{ + t = Main.elapsedTime; + FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInt, 2)})}); + (Main.elapsedTime - t).postln; +}.play +) + +//look at the retrieved formatted array of [pitch,confidence] values +d.postln + +//iterate and make an array of the indices which are fitting the conditions +( +e = Array.new; +d.do({ + arg val, i; + if ((val[0] > 500) && (val[1] > 0.666)) {e = e.add(i)}; // if pitch is greater than 500Hz and confidence higher than 0.666, keep the index +}); +) +e.postln; + +//granulate only the frames that are in our buffer +// We need to convert our indices to frame start. Their position was (index * hopSize) - (winSize) in FluidBufPitch +f = e.collect({arg i; (i * 512) - 1024}); + +// define a basic grain synth +( +SynthDef(\grain, + { arg out=0, buf =0 , ind = 0, pan = 0; + var env; + env = EnvGen.kr(Env.new([0,1,0],[512/s.sampleRate].dup,\sine), doneAction: Done.freeSelf); + Out.ar(out, Pan2.ar(PlayBuf.ar(1,buf,startPos:ind),pan)); + }).add; +) + +// start the sequence +( +a = Pxrand(f, inf).asStream; +Routine({ + loop({ + Synth(\grain, [\buf, b, \ind, a.next, \pan, (2.0.rand - 1)]); + (256/s.sampleRate).wait; + }) +}).play; +) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index 8577132..fee169c 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -205,7 +205,8 @@ f.free; // more fun: processing the 3 rank independently ( -f = {var source, x,y,z, rev, dist, bases = c.bufnum; +f = {arg bases = c.bufnum; + var source, x,y,z, rev, dist; source = In.ar(0,2); #x,y,z = FluidNMFFilter.ar(source.sum, bases, 3, winSize:2048); rev = FreeVerb.ar(x); diff --git a/release-packaging/HelpSource/Classes/FluidPitch.schelp b/release-packaging/HelpSource/Classes/FluidPitch.schelp index 257dda8..3deb8a4 100644 --- a/release-packaging/HelpSource/Classes/FluidPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidPitch.schelp @@ -17,14 +17,13 @@ ARGUMENT:: in The audio to be processed. ARGUMENT:: algorithm - The algorithm to estimate the pitch. The options are: + The algorithm to estimate the pitch. The options are: TABLE:: - ## 0 || Cepstrum: TODO. - ## 1 || Harmonic Product Spectrum: TODO. - ## 2 || YinFFT: TODO. + ## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC). + ## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 :: + ## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications.” QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html :: - ARGUMENT:: 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 From 2bda50332398246779e6fbe42e4f20f8a8393d41 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 8 May 2019 11:47:03 +0100 Subject: [PATCH 18/50] corrected the error handling in all buf* objects --- release-packaging/Classes/FluidBufCompose.sc | 4 ++-- release-packaging/Classes/FluidBufHPSS.sc | 2 +- release-packaging/Classes/FluidBufNMF.sc | 2 +- release-packaging/Classes/FluidBufNoveltySlice.sc | 4 ++-- release-packaging/Classes/FluidBufOnsetSlice.sc | 4 ++-- release-packaging/Classes/FluidBufPitch.sc | 4 ++-- release-packaging/Classes/FluidBufSines.sc | 2 +- release-packaging/Classes/FluidBufTransientSlice.sc | 4 ++-- release-packaging/Classes/FluidBufTransients.sc | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/release-packaging/Classes/FluidBufCompose.sc b/release-packaging/Classes/FluidBufCompose.sc index 9e7c3ce..1e4a569 100644 --- a/release-packaging/Classes/FluidBufCompose.sc +++ b/release-packaging/Classes/FluidBufCompose.sc @@ -4,8 +4,8 @@ FluidBufCompose{ source = source.asUGenInput; destination = destination.asUGenInput; - 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}; + source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; server = server ? Server.default; diff --git a/release-packaging/Classes/FluidBufHPSS.sc b/release-packaging/Classes/FluidBufHPSS.sc index b0c53ee..6d4882f 100644 --- a/release-packaging/Classes/FluidBufHPSS.sc +++ b/release-packaging/Classes/FluidBufHPSS.sc @@ -8,7 +8,7 @@ FluidBufHPSS{ percussive = percussive.asUGenInput; residual = residual.asUGenInput; - if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; server = server ? Server.default; harmonic = harmonic ? -1; diff --git a/release-packaging/Classes/FluidBufNMF.sc b/release-packaging/Classes/FluidBufNMF.sc index 92b1f07..8b166bd 100644 --- a/release-packaging/Classes/FluidBufNMF.sc +++ b/release-packaging/Classes/FluidBufNMF.sc @@ -7,7 +7,7 @@ FluidBufNMF { bases = bases.asUGenInput; activations = activations.asUGenInput; - if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; server = server ? Server.default; diff --git a/release-packaging/Classes/FluidBufNoveltySlice.sc b/release-packaging/Classes/FluidBufNoveltySlice.sc index 64bb56d..b7b934e 100644 --- a/release-packaging/Classes/FluidBufNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufNoveltySlice.sc @@ -6,8 +6,8 @@ FluidBufNoveltySlice{ source = source.asUGenInput; indices = indices.asUGenInput; - 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}; + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; server = server ? Server.default; diff --git a/release-packaging/Classes/FluidBufOnsetSlice.sc b/release-packaging/Classes/FluidBufOnsetSlice.sc index 795390f..2c4dcb0 100644 --- a/release-packaging/Classes/FluidBufOnsetSlice.sc +++ b/release-packaging/Classes/FluidBufOnsetSlice.sc @@ -6,8 +6,8 @@ FluidBufOnsetSlice{ source = source.asUGenInput; indices = indices.asUGenInput; - 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}; + source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; server = server ? Server.default; diff --git a/release-packaging/Classes/FluidBufPitch.sc b/release-packaging/Classes/FluidBufPitch.sc index 5780e93..566be36 100644 --- a/release-packaging/Classes/FluidBufPitch.sc +++ b/release-packaging/Classes/FluidBufPitch.sc @@ -6,8 +6,8 @@ FluidBufPitch{ source = source.asUGenInput; features = features.asUGenInput; - source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw}; - features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw}; + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; server = server ? Server.default; diff --git a/release-packaging/Classes/FluidBufSines.sc b/release-packaging/Classes/FluidBufSines.sc index 2f1a8a3..d54520a 100644 --- a/release-packaging/Classes/FluidBufSines.sc +++ b/release-packaging/Classes/FluidBufSines.sc @@ -7,7 +7,7 @@ FluidBufSines{ sines = sines.asUGenInput; residual = residual.asUGenInput; - if(source.isNil) {Error("Invalid Buffer").format(thisMethod.name, this.class.name).throw}; + source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; server = server ? Server.default; sines = sines ? -1; diff --git a/release-packaging/Classes/FluidBufTransientSlice.sc b/release-packaging/Classes/FluidBufTransientSlice.sc index 69e3556..f4b6d7f 100644 --- a/release-packaging/Classes/FluidBufTransientSlice.sc +++ b/release-packaging/Classes/FluidBufTransientSlice.sc @@ -4,8 +4,8 @@ FluidBufTransientSlice{ source = source.asUGenInput; indices = indices.asUGenInput; - 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}; + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; server = server ? Server.default; diff --git a/release-packaging/Classes/FluidBufTransients.sc b/release-packaging/Classes/FluidBufTransients.sc index ad817cf..7a2cad9 100644 --- a/release-packaging/Classes/FluidBufTransients.sc +++ b/release-packaging/Classes/FluidBufTransients.sc @@ -5,7 +5,7 @@ FluidBufTransients { transients = transients.asUGenInput; residual = residual.asUGenInput; - if(source.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + source.isNil.if {"FluidBufTransients: Invalid source buffer".throw}; server = server ? Server.default; transients = transients ? -1; From 1f96df851a5cdb6e3a95a0d25028d1485243bc89 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 9 May 2019 11:16:20 +0100 Subject: [PATCH 19/50] few typos sorted for alpha05 --- release-packaging/HelpSource/Classes/FluidNMFFilter.schelp | 6 +++++- release-packaging/HelpSource/Classes/FluidPitch.schelp | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index fee169c..958bdb5 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -119,7 +119,7 @@ Routine { // wait for the query to print // then find the rank that has more sustain pitch than pick (TODO: use descriptors with stats) ( - ~element = 1; + ~element = 4; {PlayBuf.ar(10,c)[~element]}.play; ) @@ -215,10 +215,14 @@ f = {arg bases = c.bufnum; }.play(addAction:\addToTail); ) +// set the bases +f.set(\bases, c.bufnum) + // here you can retrigger the factorisation g = Buffer.alloc(s,1025,3); FluidBufNMF.process(s, d, bases:g, winSize:2048, rank:3); f.set(\bases, g.bufnum) + //free f.free; e.free; :: diff --git a/release-packaging/HelpSource/Classes/FluidPitch.schelp b/release-packaging/HelpSource/Classes/FluidPitch.schelp index 3deb8a4..44b1c52 100644 --- a/release-packaging/HelpSource/Classes/FluidPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidPitch.schelp @@ -92,8 +92,7 @@ x = { }.play; ) -// the built-in is slightly worse on pure sinewaves -x.set(\freq, 220) +// the built-in is slightly better on pure sinewaves x.set(\freq, 440) // adding harmonics, by changing to triangle (1), saw (2) or square (3) shows that spectral algo are more resilient when signal are richer @@ -103,6 +102,8 @@ x.set(\type, 3) // adding noise shows the comparative sturdiness of the spectral pitch tracker x.set(\noise, 0.05) + +//if latency is no issue, getting a higher winSize will stabilise the algorithm even more :: STRONG::a more musical example:: @@ -149,5 +150,4 @@ f = { ReplaceOut.ar(0, source + [delay1+delay3,delay2+delay3]); }.play(addAction:\addToTail); ) - :: From 85f56dc2bab8dbd71fa54466c7d0bf0e6659bd81 Mon Sep 17 00:00:00 2001 From: Gerard Date: Mon, 13 May 2019 10:03:12 +0200 Subject: [PATCH 20/50] adding FluidBufStats --- src/FluidBufStats/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidBufStats/FluidBufStats.cpp | 14 ++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/FluidBufStats/CMakeLists.txt create mode 100644 src/FluidBufStats/FluidBufStats.cpp diff --git a/src/FluidBufStats/CMakeLists.txt b/src/FluidBufStats/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidBufStats/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufStats/FluidBufStats.cpp b/src/FluidBufStats/FluidBufStats.cpp new file mode 100644 index 0000000..fddfaf3 --- /dev/null +++ b/src/FluidBufStats/FluidBufStats.cpp @@ -0,0 +1,14 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(OfflineFluidDecompositionUGens) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("BufStats", ft); +} From 23db2fd94710e4aa6a8eb8e060c09eadb70f2290 Mon Sep 17 00:00:00 2001 From: Gerard Date: Mon, 13 May 2019 12:53:36 +0200 Subject: [PATCH 21/50] adding FluidMelBands, FluidBufMelBands --- src/FluidBufMelBands/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidBufMelBands/FluidBufMelBands.cpp | 13 +++++++++++++ src/FluidMelBands/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidMelBands/FluidMelBands.cpp | 13 +++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 src/FluidBufMelBands/CMakeLists.txt create mode 100644 src/FluidBufMelBands/FluidBufMelBands.cpp create mode 100644 src/FluidMelBands/CMakeLists.txt create mode 100644 src/FluidMelBands/FluidMelBands.cpp diff --git a/src/FluidBufMelBands/CMakeLists.txt b/src/FluidBufMelBands/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidBufMelBands/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufMelBands/FluidBufMelBands.cpp b/src/FluidBufMelBands/FluidBufMelBands.cpp new file mode 100644 index 0000000..9e4a6cb --- /dev/null +++ b/src/FluidBufMelBands/FluidBufMelBands.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("BufMelBands", ft); +} diff --git a/src/FluidMelBands/CMakeLists.txt b/src/FluidMelBands/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidMelBands/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidMelBands/FluidMelBands.cpp b/src/FluidMelBands/FluidMelBands.cpp new file mode 100644 index 0000000..8881261 --- /dev/null +++ b/src/FluidMelBands/FluidMelBands.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidMelBands", ft); +} From 0fb414af7dbad88aba4b4ba71df1a2c57e2d6936 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Mon, 13 May 2019 16:23:26 +0100 Subject: [PATCH 22/50] bufstats class def and help file in progress --- release-packaging/Classes/FluidBufStats.sc | 19 +++++ .../HelpSource/Classes/FluidBufStats.schelp | 70 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 release-packaging/Classes/FluidBufStats.sc create mode 100644 release-packaging/HelpSource/Classes/FluidBufStats.schelp diff --git a/release-packaging/Classes/FluidBufStats.sc b/release-packaging/Classes/FluidBufStats.sc new file mode 100644 index 0000000..806b62e --- /dev/null +++ b/release-packaging/Classes/FluidBufStats.sc @@ -0,0 +1,19 @@ +FluidBufStats{ + *process { arg server, source, stats, numDerivatives = 0, low = 0, middle = 50, high = 100, action; + + source = source.asUGenInput; + stats = stats.asUGenInput; + + source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; + stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; + + server = server ? Server.default; + + forkIfNeeded{ + server.sendMsg(\cmd, \BufStats, source, stats, numDerivatives, low, middle, high); + server.sync; + stats = server.cachedBufferAt(stats); stats.updateInfo; server.sync; + action.value(stats); + }; + } +} diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp new file mode 100644 index 0000000..090e6d3 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -0,0 +1,70 @@ +TITLE:: FluidBufStats +SUMMARY:: Buffer-Based Novelty-Based Slicer +CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition + + +DESCRIPTION:: +This class implements a non-real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European 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 processed. The different channels of multichannel buffers will be considered independently as time series. + +ARGUMENT:: stats +(describe argument here) + +ARGUMENT:: numDerivatves +(describe argument here) + +ARGUMENT:: low +(describe argument here) + +ARGUMENT:: middle +(describe argument here) + +ARGUMENT:: high +(describe argument here) + +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 destination buffer is declared in the function call. + +EXAMPLES:: + +CODE:: + +// make a buffer of known lenght +b = Buffer.alloc(s,7); + +// add known values + b.setn(0, Array.fill(7,{|i|i / 6})); + +// create a new buffer as destinations +c = Buffer.new(s); + +//run the process on them +( +// with basic params +Routine{ + t = Main.elapsedTime; + FluidBufStats.process(s,b, stats: c); + (Main.elapsedTime - t).postln; +}.play +) + +// list the indicies of detected attacks - the two input channels have been summed +c.getn(0,c.numFrames,{|item|item.postln;}) +:: From e125ad692afbfa759f70bf3b2b2e513befae5628 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 14 May 2019 11:40:37 +0100 Subject: [PATCH 23/50] classes definitions, and skeletons of helpfiles/testfiles for (buf)mfcc and (buf)melbands --- release-packaging/Classes/FluidBufMFCC.sc | 25 ++ release-packaging/Classes/FluidBufMelBands.sc | 25 ++ release-packaging/Classes/FluidMFCC.sc | 20 ++ release-packaging/Classes/FluidMelBands.sc | 20 ++ .../HelpSource/Classes/FluidBufMFCC.schelp | 131 +++++++++ .../Classes/FluidBufMelBands.schelp | 128 +++++++++ .../HelpSource/Classes/FluidMFCC.schelp | 258 ++++++++++++++++++ .../HelpSource/Classes/FluidMelBands.schelp | 255 +++++++++++++++++ 8 files changed, 862 insertions(+) create mode 100644 release-packaging/Classes/FluidBufMFCC.sc create mode 100644 release-packaging/Classes/FluidBufMelBands.sc create mode 100644 release-packaging/Classes/FluidMFCC.sc create mode 100644 release-packaging/Classes/FluidMelBands.sc create mode 100644 release-packaging/HelpSource/Classes/FluidBufMFCC.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidBufMelBands.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidMFCC.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidMelBands.schelp diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc new file mode 100644 index 0000000..6a6c820 --- /dev/null +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -0,0 +1,25 @@ +FluidBufMFCC{ + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, winSize = 1024, hopSize = -1, fftSize = -1, action; + + var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + + server = server ? Server.default; + + //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) + // same goes to maxNumCoefs, which is passed numCoefs in this case + + forkIfNeeded{ + server.sendMsg(\cmd, \BufMFCC, source, startFrame, numFrames, startChan, numChans, features, numCoefs, numBands, minFreq, maxFreq, numCoefs, winSize, hopSize, fftSize, maxFFTSize); + server.sync; + features = server.cachedBufferAt(features); features.updateInfo; server.sync; + action.value(features); + }; + } +} diff --git a/release-packaging/Classes/FluidBufMelBands.sc b/release-packaging/Classes/FluidBufMelBands.sc new file mode 100644 index 0000000..33ef24b --- /dev/null +++ b/release-packaging/Classes/FluidBufMelBands.sc @@ -0,0 +1,25 @@ +FluidBufMelBands{ + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, winSize = 1024, hopSize = -1, fftSize = -1, action; + + var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + + server = server ? Server.default; + + //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) + // same for maxNumBands which is passed numBands + + forkIfNeeded{ + server.sendMsg(\cmd, \BufMelBands, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, winSize, hopSize, fftSize, maxFFTSize); + server.sync; + features = server.cachedBufferAt(features); features.updateInfo; server.sync; + action.value(features); + }; + } +} diff --git a/release-packaging/Classes/FluidMFCC.sc b/release-packaging/Classes/FluidMFCC.sc new file mode 100644 index 0000000..222d72f --- /dev/null +++ b/release-packaging/Classes/FluidMFCC.sc @@ -0,0 +1,20 @@ +FluidMFCC : MultiOutUGen { + + *kr { arg in = 0, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumCoefs = 40, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), numCoefs, numBands, minFreq, maxFreq, maxNumCoefs, winSize, hopSize, fftSize, maxFFTSize); + } + + init {arg ...theInputs; + inputs = theInputs; + ^this.initOutputs(inputs.at(5),rate); + } + + checkInputs { + if(inputs.at(5).rate != 'scalar') { + ^(": maxNumCoefs cannot be modulated."); + }; + if(inputs.at(9).rate != 'scalar') { + ^(": maxFFTSize cannot be modulated."); + };^this.checkValidInputs; + } +} diff --git a/release-packaging/Classes/FluidMelBands.sc b/release-packaging/Classes/FluidMelBands.sc new file mode 100644 index 0000000..195d9b5 --- /dev/null +++ b/release-packaging/Classes/FluidMelBands.sc @@ -0,0 +1,20 @@ +FluidMelBands : MultiOutUGen { + + *kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 40, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), numBands, minFreq, maxFreq, maxNumBands, winSize, hopSize, fftSize, maxFFTSize); + } + + init {arg ...theInputs; + inputs = theInputs; + ^this.initOutputs(inputs.at(4),rate); + } + + checkInputs { + if(inputs.at(4).rate != 'scalar') { + ^(": maxNumCoefs cannot be modulated."); + }; + if(inputs.at(8).rate != 'scalar') { + ^(": maxFFTSize cannot be modulated."); + };^this.checkValidInputs; + } +} diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp new file mode 100644 index 0000000..3fc55c4 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -0,0 +1,131 @@ +TITLE:: FluidBufMFCC +SUMMARY:: Seven Spectral Shape Descriptors on a Buffer +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile + + +DESCRIPTION:: +This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: +LIST:: +##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: + LIST:: + ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. + ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. + ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. + ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. + :: + ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. + ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. + ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. + + The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. +:: + + The process will return a multichannel buffer with the seven channels per input channel, each containing the 7 shapes. Each sample represents a value, which is every hopSize. + +CLASSMETHODS:: + +METHOD:: process + This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. + +ARGUMENT:: server + The server on which the buffers to be processed are allocated. + +ARGUMENT:: source + The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially. + +ARGUMENT:: startFrame + Where in the srcBuf should the process start, in sample. + +ARGUMENT:: numFrames + How many frames should be processed. + +ARGUMENT:: startChan + For multichannel srcBuf, which channel should be processed first. + +ARGUMENT:: numChans + For multichannel srcBuf, how many channel should be processed. + +ARGUMENT:: features + The destination buffer for the 7 spectral features describing the spectral shape. + +ARGUMENT:: numCoefs +(describe argument here) + +ARGUMENT:: numBands +(describe argument here) + +ARGUMENT:: minFreq +(describe argument here) + +ARGUMENT:: maxFreq +(describe argument here) + +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 + +ARGUMENT:: hopSize + The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + +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 [features] as an argument. + +RETURNS:: + Nothing, as the destination buffer is declared in the function call. + +EXAMPLES:: + +code:: +// create some buffers +( +b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +c = Buffer.new(s); +) + +// run the process with basic parameters +( +Routine{ + t = Main.elapsedTime; + FluidBufSpectralShape.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// listen to the source and look at the buffer +b.play; +c.plot(minval:-5, maxval:250) +:: + +STRONG::A stereo buffer example.:: +CODE:: + +// load two very different files +( +b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav"); +c = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +) + +// composite one on left one on right as test signals +FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) +b.play + +// create a buffer as destinations +c = Buffer.new(s); + +//run the process on them +( +Routine{ + t = Main.elapsedTime; + FluidBufSpectralShape.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// look at the buffer: 7shapes for left, then 7 shapes for right +c.plot(minval:-25, maxval:150) +:: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp new file mode 100644 index 0000000..e48313f --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -0,0 +1,128 @@ +TITLE:: FluidBufMelBands +SUMMARY:: Seven Spectral Shape Descriptors on a Buffer +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile + + +DESCRIPTION:: +This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: +LIST:: +##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: + LIST:: + ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. + ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. + ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. + ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. + :: + ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. + ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. + ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. + + The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. +:: + + The process will return a multichannel buffer with the seven channels per input channel, each containing the 7 shapes. Each sample represents a value, which is every hopSize. + +CLASSMETHODS:: + +METHOD:: process + This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. + +ARGUMENT:: server + The server on which the buffers to be processed are allocated. + +ARGUMENT:: source + The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially. + +ARGUMENT:: startFrame + Where in the srcBuf should the process start, in sample. + +ARGUMENT:: numFrames + How many frames should be processed. + +ARGUMENT:: startChan + For multichannel srcBuf, which channel should be processed first. + +ARGUMENT:: numChans + For multichannel srcBuf, how many channel should be processed. + +ARGUMENT:: features + The destination buffer for the 7 spectral features describing the spectral shape. + +ARGUMENT:: numBands +(describe argument here) + +ARGUMENT:: minFreq +(describe argument here) + +ARGUMENT:: maxFreq +(describe argument here) + +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 + +ARGUMENT:: hopSize + The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + +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 [features] as an argument. + +RETURNS:: + Nothing, as the destination buffer is declared in the function call. + +EXAMPLES:: + +code:: +// create some buffers +( +b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +c = Buffer.new(s); +) + +// run the process with basic parameters +( +Routine{ + t = Main.elapsedTime; + FluidBufSpectralShape.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// listen to the source and look at the buffer +b.play; +c.plot(minval:-5, maxval:250) +:: + +STRONG::A stereo buffer example.:: +CODE:: + +// load two very different files +( +b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav"); +c = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +) + +// composite one on left one on right as test signals +FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) +b.play + +// create a buffer as destinations +c = Buffer.new(s); + +//run the process on them +( +Routine{ + t = Main.elapsedTime; + FluidBufSpectralShape.process(s, b, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// look at the buffer: 7shapes for left, then 7 shapes for right +c.plot(minval:-25, maxval:150) +:: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp new file mode 100644 index 0000000..ece0a1d --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidMFCC.schelp @@ -0,0 +1,258 @@ +TITLE:: FluidMFCC +SUMMARY:: Seven Spectral Shape Descriptors in Real-Time +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile + +DESCRIPTION:: +This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: +LIST:: +##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: + LIST:: + ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. + ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. + ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. + ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. + :: + ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. + ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. + ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. + + The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. +:: + + The process will return a multichannel control steam with the seven values, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. + +CLASSMETHODS:: + +METHOD:: kr + The audio rate in, control rate out version of the object. + +ARGUMENT:: in + The audio to be processed. + +ARGUMENT:: numCoefs +(describe argument here) + +ARGUMENT:: numBands +(describe argument here) + +ARGUMENT:: minFreq +(describe argument here) + +ARGUMENT:: maxFreq +(describe argument here) + +ARGUMENT:: maxNumCoefs +(describe argument here) + +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 + +ARGUMENT:: hopSize + The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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:: maxFFTSize + How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. + +RETURNS:: + A 7-channel KR signal with the seven spectral shape descriptors. The latency is winSize. + + +EXAMPLES:: + + +code:: +//create a monitoring bus for the descriptors +b = Bus.new(\control,0,7); + +//create a monitoring window for the values + +( +w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; + +c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c[0].string = ("Centroid: "); +c[1].string = ("Spread: "); +c[2].string = ("Skewness: "); +c[3].string = ("Kurtosis: "); +c[4].string = ("Rolloff: "); +c[5].string = ("Flatness: "); +c[6].string = ("Crest: "); + +a = Array.fill(7, {arg i; + StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); +}); +) + +//run the wondow updating routine. +( +r = Routine { + { + + b.get({ arg val; + { + if(w.isClosed.not) { + val.do({arg item,index; + a[index].string = item.round(0.01)}) + } + }.defer + }); + + 0.01.wait; + }.loop + +}.play +) + +//play a simple sound to observe the values +( + { + var source; + source = BPF.ar(WhiteNoise.ar(), 330, 55/330); + Out.kr(b,FluidSpectralShape.kr(source)); + source.dup; + }.play; +) +:: + +STRONG::A commented tutorial on how each descriptor behaves with test signals: :: + +CODE:: +// as above, create a monitoring bus for the descriptors +b = Bus.new(\control,0,7); + +//again, create a monitoring window for the values +( +w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; + +c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c[0].string = ("Centroid: "); +c[1].string = ("Spread: "); +c[2].string = ("Skewness: "); +c[3].string = ("Kurtosis: "); +c[4].string = ("Rolloff: "); +c[5].string = ("Flatness: "); +c[6].string = ("Crest: "); + +a = Array.fill(7, {arg i; + StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); +}); +) + +// this time, update a little more slowly, and convert in Hz the 3 descriptors published in bins by the algorythm. +( +r = Routine { + { + + b.get({ arg val; + { + if(w.isClosed.not) { + val.do({arg item,index; + if ((index < 2) || (index == 4)) + { + a[index].string = (item * s.sampleRate / 1024).round(0.01); + } { + a[index].string = item.round(0.01); + }; + }) + } + }.defer + }); + + 0.2.wait; + }.loop + +}.play +) + +// first, a sine wave +( +x = { + arg freq=220; + var source; + source = SinOsc.ar(freq,mul:0.1); + Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); + source.dup; +}.play; +) + +// at 220, the centroid is on the frequency, the spread is narrow, but as wide as the FFT Hann window ripples, the skewness is high as we are low and therefore far left of the middle bin (aka half-Nyquist), the Kurtosis is incredibly high as we have a very peaky spectrum. The rolloff is slightly higher than the frequency, taking into account the FFT windowing ripples, the flatness is incredibly low, as we have one peak and not much else, and the crest is quite high, because most of the energy is in a few peaky bins. + +x.set(\freq, 440) + +// at 440, the skewness has changed (we are nearer the middle of the spectrogram) and the Kurtosis too, although it is still so high it is quite in the same order of magnitude. The rest is stable, as expected. + +x.set(\freq, 11000) + +// at 11kHz, kurtosis is still in the thousand, but skewness is almost null, as expected. + +x.free + +// second, broadband noise +( +x = { + arg type = 0; + var source; + source = Select.ar(type,[WhiteNoise.ar(0.1),PinkNoise.ar(0.1)]); + Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); + source.dup; +}.play; +) + +// white noise has a linear repartition of energy, so we would expect a centroid in the middle bin (aka half-Nyquist) with a spread covering the full range (+/- a quarter-Nyquist), with a skewness almost null since we are centered, and a very low Kurtosis since we are flat. The rolloff should be almost at Nyquist, the flatness as high as it gets, and the crest quite low. + +x.set(\type, 1) + +// pink noise has a drop of 3dB per octave across the spectrum, so we would, by comparison, expect a lower centroid, a slighly higher skewness and kurtosis, a lower rolloff, a slighly lower flatness and a higher crest for the larger low-end energy. + +x.free + +// third, bands of noise +( +x = { + arg type = 0; + var source, chain; + chain = FFT(LocalBuf(1024), WhiteNoise.ar(0.5)); + chain = chain.pvcollect(1024, {arg mag,phase;[mag,phase]},5,11,1); + source = Select.ar(type,[ + BPF.ar(BPF.ar(WhiteNoise.ar(0.5),330,0.666),330,0.666), + IFFT(chain)]); + Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); + source.dup; +}.play; +) + +// a second-order bandpass filter on whitenoise, centred on 330Hz with one octave bandwidth, gives us a centroid quite high. This is due to the exponential behaviour of the filter, with a gentle slope. Observe the spectral analyser: + +s.freqscope + +// at first it seems quite centred, but then flip the argument FrqScl to lin(ear) and observe how high the spectrum goes. If we set it to a brickwall spectral filter tuned on the same frequencies: + +x.set(\type, 1) + +// we have a much narrower register, and our centroid and spread, as well as the kurtosis and flatness, agrees with this reading. + +x.free + +//fourth, equally spaced sines +( +x = { + arg freq = 220; + var source; + source = Mix.fill(7, {arg ind; SinOsc.ar(freq + (ind * (220 / 6)), 0, 0.02)}); + Out.kr(b,FluidSpectralShape.kr(source)); + source.dup; +}.play; +) + +// this example shows a similar result to the brickwall spectral bandpass above. If we move the central frequency nearer the half-Nyquist: + +x.set(\freq, 8800) + +// we can observe that the linear spread is kept the same, since there is the same linear distance in Hz between our frequencies. Skewness is a good indication here of where we are in the spectrum with the shape. +:: diff --git a/release-packaging/HelpSource/Classes/FluidMelBands.schelp b/release-packaging/HelpSource/Classes/FluidMelBands.schelp new file mode 100644 index 0000000..a404532 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidMelBands.schelp @@ -0,0 +1,255 @@ +TITLE:: FluidMelBands +SUMMARY:: Seven Spectral Shape Descriptors in Real-Time +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile + +DESCRIPTION:: +This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: +LIST:: +##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: + LIST:: + ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. + ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. + ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. + ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. + :: + ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. + ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. + ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. + + The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. +:: + + The process will return a multichannel control steam with the seven values, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. + +CLASSMETHODS:: + +METHOD:: kr + The audio rate in, control rate out version of the object. + +ARGUMENT:: in + The audio to be processed. + +ARGUMENT:: numBands +(describe argument here) + +ARGUMENT:: minFreq +(describe argument here) + +ARGUMENT:: maxFreq +(describe argument here) + +ARGUMENT:: maxNumBands +(describe argument here) + +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 + +ARGUMENT:: hopSize + The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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:: maxFFTSize + How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. + +RETURNS:: + A 7-channel KR signal with the seven spectral shape descriptors. The latency is winSize. + + +EXAMPLES:: + + +code:: +//create a monitoring bus for the descriptors +b = Bus.new(\control,0,7); + +//create a monitoring window for the values + +( +w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; + +c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c[0].string = ("Centroid: "); +c[1].string = ("Spread: "); +c[2].string = ("Skewness: "); +c[3].string = ("Kurtosis: "); +c[4].string = ("Rolloff: "); +c[5].string = ("Flatness: "); +c[6].string = ("Crest: "); + +a = Array.fill(7, {arg i; + StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); +}); +) + +//run the wondow updating routine. +( +r = Routine { + { + + b.get({ arg val; + { + if(w.isClosed.not) { + val.do({arg item,index; + a[index].string = item.round(0.01)}) + } + }.defer + }); + + 0.01.wait; + }.loop + +}.play +) + +//play a simple sound to observe the values +( + { + var source; + source = BPF.ar(WhiteNoise.ar(), 330, 55/330); + Out.kr(b,FluidSpectralShape.kr(source)); + source.dup; + }.play; +) +:: + +STRONG::A commented tutorial on how each descriptor behaves with test signals: :: + +CODE:: +// as above, create a monitoring bus for the descriptors +b = Bus.new(\control,0,7); + +//again, create a monitoring window for the values +( +w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; + +c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c[0].string = ("Centroid: "); +c[1].string = ("Spread: "); +c[2].string = ("Skewness: "); +c[3].string = ("Kurtosis: "); +c[4].string = ("Rolloff: "); +c[5].string = ("Flatness: "); +c[6].string = ("Crest: "); + +a = Array.fill(7, {arg i; + StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); +}); +) + +// this time, update a little more slowly, and convert in Hz the 3 descriptors published in bins by the algorythm. +( +r = Routine { + { + + b.get({ arg val; + { + if(w.isClosed.not) { + val.do({arg item,index; + if ((index < 2) || (index == 4)) + { + a[index].string = (item * s.sampleRate / 1024).round(0.01); + } { + a[index].string = item.round(0.01); + }; + }) + } + }.defer + }); + + 0.2.wait; + }.loop + +}.play +) + +// first, a sine wave +( +x = { + arg freq=220; + var source; + source = SinOsc.ar(freq,mul:0.1); + Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); + source.dup; +}.play; +) + +// at 220, the centroid is on the frequency, the spread is narrow, but as wide as the FFT Hann window ripples, the skewness is high as we are low and therefore far left of the middle bin (aka half-Nyquist), the Kurtosis is incredibly high as we have a very peaky spectrum. The rolloff is slightly higher than the frequency, taking into account the FFT windowing ripples, the flatness is incredibly low, as we have one peak and not much else, and the crest is quite high, because most of the energy is in a few peaky bins. + +x.set(\freq, 440) + +// at 440, the skewness has changed (we are nearer the middle of the spectrogram) and the Kurtosis too, although it is still so high it is quite in the same order of magnitude. The rest is stable, as expected. + +x.set(\freq, 11000) + +// at 11kHz, kurtosis is still in the thousand, but skewness is almost null, as expected. + +x.free + +// second, broadband noise +( +x = { + arg type = 0; + var source; + source = Select.ar(type,[WhiteNoise.ar(0.1),PinkNoise.ar(0.1)]); + Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); + source.dup; +}.play; +) + +// white noise has a linear repartition of energy, so we would expect a centroid in the middle bin (aka half-Nyquist) with a spread covering the full range (+/- a quarter-Nyquist), with a skewness almost null since we are centered, and a very low Kurtosis since we are flat. The rolloff should be almost at Nyquist, the flatness as high as it gets, and the crest quite low. + +x.set(\type, 1) + +// pink noise has a drop of 3dB per octave across the spectrum, so we would, by comparison, expect a lower centroid, a slighly higher skewness and kurtosis, a lower rolloff, a slighly lower flatness and a higher crest for the larger low-end energy. + +x.free + +// third, bands of noise +( +x = { + arg type = 0; + var source, chain; + chain = FFT(LocalBuf(1024), WhiteNoise.ar(0.5)); + chain = chain.pvcollect(1024, {arg mag,phase;[mag,phase]},5,11,1); + source = Select.ar(type,[ + BPF.ar(BPF.ar(WhiteNoise.ar(0.5),330,0.666),330,0.666), + IFFT(chain)]); + Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); + source.dup; +}.play; +) + +// a second-order bandpass filter on whitenoise, centred on 330Hz with one octave bandwidth, gives us a centroid quite high. This is due to the exponential behaviour of the filter, with a gentle slope. Observe the spectral analyser: + +s.freqscope + +// at first it seems quite centred, but then flip the argument FrqScl to lin(ear) and observe how high the spectrum goes. If we set it to a brickwall spectral filter tuned on the same frequencies: + +x.set(\type, 1) + +// we have a much narrower register, and our centroid and spread, as well as the kurtosis and flatness, agrees with this reading. + +x.free + +//fourth, equally spaced sines +( +x = { + arg freq = 220; + var source; + source = Mix.fill(7, {arg ind; SinOsc.ar(freq + (ind * (220 / 6)), 0, 0.02)}); + Out.kr(b,FluidSpectralShape.kr(source)); + source.dup; +}.play; +) + +// this example shows a similar result to the brickwall spectral bandpass above. If we move the central frequency nearer the half-Nyquist: + +x.set(\freq, 8800) + +// we can observe that the linear spread is kept the same, since there is the same linear distance in Hz between our frequencies. Skewness is a good indication here of where we are in the spectrum with the shape. +:: From a77c46c1981dcb9956844c182814f989a7c18974 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 14 May 2019 12:20:11 +0100 Subject: [PATCH 24/50] further melbands exploration and help --- release-packaging/Classes/FluidBufMelBands.sc | 4 +- release-packaging/Classes/FluidMelBands.sc | 4 +- .../HelpSource/Classes/FluidMelBands.schelp | 171 ++---------------- .../Classes/FluidSpectralShape.schelp | 4 +- 4 files changed, 18 insertions(+), 165 deletions(-) diff --git a/release-packaging/Classes/FluidBufMelBands.sc b/release-packaging/Classes/FluidBufMelBands.sc index 33ef24b..2b7c770 100644 --- a/release-packaging/Classes/FluidBufMelBands.sc +++ b/release-packaging/Classes/FluidBufMelBands.sc @@ -6,8 +6,8 @@ FluidBufMelBands{ source = source.asUGenInput; features = features.asUGenInput; - source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; server = server ? Server.default; diff --git a/release-packaging/Classes/FluidMelBands.sc b/release-packaging/Classes/FluidMelBands.sc index 195d9b5..5055791 100644 --- a/release-packaging/Classes/FluidMelBands.sc +++ b/release-packaging/Classes/FluidMelBands.sc @@ -1,6 +1,6 @@ FluidMelBands : MultiOutUGen { - *kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 40, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + *kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 120, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; ^this.multiNew('control', in.asAudioRateInput(this), numBands, minFreq, maxFreq, maxNumBands, winSize, hopSize, fftSize, maxFFTSize); } @@ -11,7 +11,7 @@ FluidMelBands : MultiOutUGen { checkInputs { if(inputs.at(4).rate != 'scalar') { - ^(": maxNumCoefs cannot be modulated."); + ^(": maxNumBands cannot be modulated."); }; if(inputs.at(8).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); diff --git a/release-packaging/HelpSource/Classes/FluidMelBands.schelp b/release-packaging/HelpSource/Classes/FluidMelBands.schelp index a404532..72ec6a7 100644 --- a/release-packaging/HelpSource/Classes/FluidMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidMelBands.schelp @@ -42,7 +42,7 @@ ARGUMENT:: maxFreq (describe argument here) ARGUMENT:: maxNumBands -(describe argument here) + The maximum number of Mel bands that can be modelled. This sets the number of channels of the output, and therefore cannot be modulated. 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 @@ -65,25 +65,14 @@ EXAMPLES:: code:: //create a monitoring bus for the descriptors -b = Bus.new(\control,0,7); +b = Bus.new(\control,0,40); //create a monitoring window for the values ( -w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; - -c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("Centroid: "); -c[1].string = ("Spread: "); -c[2].string = ("Skewness: "); -c[3].string = ("Kurtosis: "); -c[4].string = ("Rolloff: "); -c[5].string = ("Flatness: "); -c[6].string = ("Crest: "); - -a = Array.fill(7, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); +w = Window("MelBands Monitor", Rect(10, 10, 620, 320)).front; + a = MultiSliderView(w,Rect(10, 10, 600, 300)); + a.valueThumbSize(1); ) //run the wondow updating routine. @@ -94,13 +83,12 @@ r = Routine { b.get({ arg val; { if(w.isClosed.not) { - val.do({arg item,index; - a[index].string = item.round(0.01)}) + a.value = val; + val.postln; } }.defer }); - - 0.01.wait; + 0.1.wait; }.loop }.play @@ -110,146 +98,11 @@ r = Routine { ( { var source; - source = BPF.ar(WhiteNoise.ar(), 330, 55/330); - Out.kr(b,FluidSpectralShape.kr(source)); + source = SinOsc.ar(220,0,0.1);//BPF.ar(WhiteNoise.ar(), 330, 55/330); + Out.kr(b,FluidMelBands.kr(source,maxNumBands:40) * 100); source.dup; }.play; ) -:: - -STRONG::A commented tutorial on how each descriptor behaves with test signals: :: - -CODE:: -// as above, create a monitoring bus for the descriptors -b = Bus.new(\control,0,7); - -//again, create a monitoring window for the values -( -w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; - -c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("Centroid: "); -c[1].string = ("Spread: "); -c[2].string = ("Skewness: "); -c[3].string = ("Kurtosis: "); -c[4].string = ("Rolloff: "); -c[5].string = ("Flatness: "); -c[6].string = ("Crest: "); - -a = Array.fill(7, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); -) - -// this time, update a little more slowly, and convert in Hz the 3 descriptors published in bins by the algorythm. -( -r = Routine { - { - - b.get({ arg val; - { - if(w.isClosed.not) { - val.do({arg item,index; - if ((index < 2) || (index == 4)) - { - a[index].string = (item * s.sampleRate / 1024).round(0.01); - } { - a[index].string = item.round(0.01); - }; - }) - } - }.defer - }); - - 0.2.wait; - }.loop - -}.play -) - -// first, a sine wave -( -x = { - arg freq=220; - var source; - source = SinOsc.ar(freq,mul:0.1); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); - source.dup; -}.play; -) - -// at 220, the centroid is on the frequency, the spread is narrow, but as wide as the FFT Hann window ripples, the skewness is high as we are low and therefore far left of the middle bin (aka half-Nyquist), the Kurtosis is incredibly high as we have a very peaky spectrum. The rolloff is slightly higher than the frequency, taking into account the FFT windowing ripples, the flatness is incredibly low, as we have one peak and not much else, and the crest is quite high, because most of the energy is in a few peaky bins. -x.set(\freq, 440) - -// at 440, the skewness has changed (we are nearer the middle of the spectrogram) and the Kurtosis too, although it is still so high it is quite in the same order of magnitude. The rest is stable, as expected. - -x.set(\freq, 11000) - -// at 11kHz, kurtosis is still in the thousand, but skewness is almost null, as expected. - -x.free - -// second, broadband noise -( -x = { - arg type = 0; - var source; - source = Select.ar(type,[WhiteNoise.ar(0.1),PinkNoise.ar(0.1)]); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); - source.dup; -}.play; -) - -// white noise has a linear repartition of energy, so we would expect a centroid in the middle bin (aka half-Nyquist) with a spread covering the full range (+/- a quarter-Nyquist), with a skewness almost null since we are centered, and a very low Kurtosis since we are flat. The rolloff should be almost at Nyquist, the flatness as high as it gets, and the crest quite low. - -x.set(\type, 1) - -// pink noise has a drop of 3dB per octave across the spectrum, so we would, by comparison, expect a lower centroid, a slighly higher skewness and kurtosis, a lower rolloff, a slighly lower flatness and a higher crest for the larger low-end energy. - -x.free - -// third, bands of noise -( -x = { - arg type = 0; - var source, chain; - chain = FFT(LocalBuf(1024), WhiteNoise.ar(0.5)); - chain = chain.pvcollect(1024, {arg mag,phase;[mag,phase]},5,11,1); - source = Select.ar(type,[ - BPF.ar(BPF.ar(WhiteNoise.ar(0.5),330,0.666),330,0.666), - IFFT(chain)]); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); - source.dup; -}.play; -) - -// a second-order bandpass filter on whitenoise, centred on 330Hz with one octave bandwidth, gives us a centroid quite high. This is due to the exponential behaviour of the filter, with a gentle slope. Observe the spectral analyser: - -s.freqscope - -// at first it seems quite centred, but then flip the argument FrqScl to lin(ear) and observe how high the spectrum goes. If we set it to a brickwall spectral filter tuned on the same frequencies: - -x.set(\type, 1) - -// we have a much narrower register, and our centroid and spread, as well as the kurtosis and flatness, agrees with this reading. - -x.free - -//fourth, equally spaced sines -( -x = { - arg freq = 220; - var source; - source = Mix.fill(7, {arg ind; SinOsc.ar(freq + (ind * (220 / 6)), 0, 0.02)}); - Out.kr(b,FluidSpectralShape.kr(source)); - source.dup; -}.play; -) - -// this example shows a similar result to the brickwall spectral bandpass above. If we move the central frequency nearer the half-Nyquist: - -x.set(\freq, 8800) - -// we can observe that the linear spread is kept the same, since there is the same linear distance in Hz between our frequencies. Skewness is a good indication here of where we are in the spectrum with the shape. -:: + {FluidMelBands.kr(SinOsc.ar(220),maxNumBands:10) * 100}.plot; +:: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp index 779453c..02b0821 100644 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp @@ -58,7 +58,7 @@ b = Bus.new(\control,0,7); //create a monitoring window for the values ( -w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; +w = Window("spectral Shape Monitor", Rect(10, 10, 220, 190)).front; c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); c[0].string = ("Centroid: "); @@ -113,7 +113,7 @@ b = Bus.new(\control,0,7); //again, create a monitoring window for the values ( -w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; +w = Window("Spectral Shape Monitor", Rect(10, 10, 220, 190)).front; c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); c[0].string = ("Centroid: "); From 82cc1dc77fba01475e01778e422c25fe737e9d18 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 14 May 2019 13:05:18 +0100 Subject: [PATCH 25/50] Set client sampling rate in time for it to be available the first time process is called include/FluidSCWrapper.hpp --- include/FluidSCWrapper.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 095b449..7a8e40c 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -97,6 +97,7 @@ public: return; } + mClient.sampleRate(fullSampleRate()); mInputConnections.reserve(mClient.audioChannelsIn()); mOutputConnections.reserve(mClient.audioChannelsOut()); mAudioInputs.reserve(mClient.audioChannelsIn()); @@ -115,11 +116,12 @@ public: } for (int i = 0; i < static_cast(mClient.controlChannelsOut()); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } - + + set_calc_function(); Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); - mClient.sampleRate(fullSampleRate()); + } @@ -137,7 +139,7 @@ public: { if (mOutputConnections[i]) mOutputs[i].reset(out(static_cast(i)), 0, fullBufferSize()); } - for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(static_cast(i)), 0, 1); } + for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(i), 0, 1); } mClient.process(mAudioInputs, mOutputs); } @@ -177,7 +179,7 @@ public: if (args->tags && ((std::string{args->tags}.size() - 1) != Client::getParameterDescriptors().count())) { - std::cout << "ERROR: " << Wrapper::getName() << " wrong number of arguments. Expected " + std::cout << "ERROR: " << Wrapper::getName() << " wrong number of arguments. Expected " << Client::getParameterDescriptors().count() << ", got " << (std::string{args->tags}.size() - 1) << ". Your .sc file and binary plugin might be different versions." << std::endl; return; From 29386e71b27de335822393418303cc3136d45593 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 14 May 2019 13:31:16 +0100 Subject: [PATCH 26/50] more work on (buf)melbands help/test --- .../HelpSource/Classes/FluidBufMelBands.schelp | 4 ++-- .../HelpSource/Classes/FluidMelBands.schelp | 15 ++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp index e48313f..8398ad4 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -88,14 +88,14 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufMelBands.process(s, b, features: c,numBands:10); (Main.elapsedTime - t).postln; }.play ) // listen to the source and look at the buffer b.play; -c.plot(minval:-5, maxval:250) +c.plot :: STRONG::A stereo buffer example.:: diff --git a/release-packaging/HelpSource/Classes/FluidMelBands.schelp b/release-packaging/HelpSource/Classes/FluidMelBands.schelp index 72ec6a7..e28773e 100644 --- a/release-packaging/HelpSource/Classes/FluidMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidMelBands.schelp @@ -70,9 +70,8 @@ b = Bus.new(\control,0,40); //create a monitoring window for the values ( -w = Window("MelBands Monitor", Rect(10, 10, 620, 320)).front; - a = MultiSliderView(w,Rect(10, 10, 600, 300)); - a.valueThumbSize(1); +w = Window("Mel Bands Monitor", Rect(10, 10, 620, 320)).front; +a = MultiSliderView(w,Rect(10, 10, 600, 300)).elasticMode_(1).isFilled_(1); ) //run the wondow updating routine. @@ -84,11 +83,10 @@ r = Routine { { if(w.isClosed.not) { a.value = val; - val.postln; } }.defer }); - 0.1.wait; + 0.01.wait; }.loop }.play @@ -98,11 +96,10 @@ r = Routine { ( { var source; - source = SinOsc.ar(220,0,0.1);//BPF.ar(WhiteNoise.ar(), 330, 55/330); - Out.kr(b,FluidMelBands.kr(source,maxNumBands:40) * 100); + // source = SinOsc.ar(220,0,0.1); + source = BPF.ar(WhiteNoise.ar(), 330, 55/330); + Out.kr(b,FluidMelBands.kr(source,maxNumBands:40)); source.dup; }.play; ) - - {FluidMelBands.kr(SinOsc.ar(220),maxNumBands:10) * 100}.plot; :: \ No newline at end of file From fa56f06fdbf6a039e942ff858df31618bba6e814 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 14 May 2019 13:39:47 +0100 Subject: [PATCH 27/50] Set params at client creation time to ensure that everything we expect happens in the client constructor include/FluidSCWrapper.hpp --- include/FluidSCWrapper.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 7a8e40c..b7219d0 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -167,9 +167,9 @@ class NonRealTime public: static void setup(InterfaceTable *ft, const char *name) { DefinePlugInCmd(name, launch, nullptr); } - NonRealTime(World*, sc_msg_iter*) + NonRealTime(World* w, sc_msg_iter* args) : mParams{Client::getParameterDescriptors()} - , mClient{mParams} + , mClient{Wrapper::setParams(mParams, false, w, args)} {} void init(){}; @@ -188,8 +188,6 @@ public: Wrapper *w = new Wrapper( world, args); // this has to be on the heap, because it doesn't get destroyed until the async command is done - Wrapper::setParams(w->mParams, false, world, args); - Result result = validateParameters(w); if (!result.ok()) { From dddc41c139322c12a67a7fc83733cf1c76ada576 Mon Sep 17 00:00:00 2001 From: Gerard Date: Tue, 14 May 2019 22:31:09 +0200 Subject: [PATCH 28/50] add FluidLoudness, FluidBufLoudness --- src/FluidBufLoudness/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidBufLoudness/FluidBufLoudness.cpp | 13 +++++++++++++ src/FluidLoudness/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidLoudness/FluidLoudness.cpp | 13 +++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 src/FluidBufLoudness/CMakeLists.txt create mode 100644 src/FluidBufLoudness/FluidBufLoudness.cpp create mode 100644 src/FluidLoudness/CMakeLists.txt create mode 100644 src/FluidLoudness/FluidLoudness.cpp diff --git a/src/FluidBufLoudness/CMakeLists.txt b/src/FluidBufLoudness/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidBufLoudness/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufLoudness/FluidBufLoudness.cpp b/src/FluidBufLoudness/FluidBufLoudness.cpp new file mode 100644 index 0000000..e0b90b1 --- /dev/null +++ b/src/FluidBufLoudness/FluidBufLoudness.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(OfflineFluidDecompositionUGens) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("BufLoudness", ft); +} diff --git a/src/FluidLoudness/CMakeLists.txt b/src/FluidLoudness/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidLoudness/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidLoudness/FluidLoudness.cpp b/src/FluidLoudness/FluidLoudness.cpp new file mode 100644 index 0000000..378fdff --- /dev/null +++ b/src/FluidLoudness/FluidLoudness.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidLoudness", ft); +} From b8cbf8767c7e4659ada2cc29d80de07e47592067 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 15 May 2019 18:58:45 +0100 Subject: [PATCH 29/50] (buf)loudness - class definition and basic help file skeleton --- release-packaging/Classes/FluidBufLoudness.sc | 21 ++++ release-packaging/Classes/FluidLoudness.sc | 17 +++ .../Classes/FluidBufLoudness.schelp | 116 ++++++++++++++++++ .../HelpSource/Classes/FluidLoudness.schelp | 100 +++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 release-packaging/Classes/FluidBufLoudness.sc create mode 100644 release-packaging/Classes/FluidLoudness.sc create mode 100644 release-packaging/HelpSource/Classes/FluidBufLoudness.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidLoudness.schelp diff --git a/release-packaging/Classes/FluidBufLoudness.sc b/release-packaging/Classes/FluidBufLoudness.sc new file mode 100644 index 0000000..402139e --- /dev/null +++ b/release-packaging/Classes/FluidBufLoudness.sc @@ -0,0 +1,21 @@ +FluidBufLoudness{ + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, winSize = 1024, hopSize = 512, action; + + var maxWinSize = winSize.nextPowerOfTwo; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + + server = server ? Server.default; + + forkIfNeeded{ + server.sendMsg(\cmd, \BufLoudness, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, winSize, hopSize, maxWinSize); + server.sync; + features = server.cachedBufferAt(features); features.updateInfo; server.sync; + action.value(features); + }; + } +} diff --git a/release-packaging/Classes/FluidLoudness.sc b/release-packaging/Classes/FluidLoudness.sc new file mode 100644 index 0000000..ef75218 --- /dev/null +++ b/release-packaging/Classes/FluidLoudness.sc @@ -0,0 +1,17 @@ +FluidLoudness : MultiOutUGen { + *kr { arg in = 0, kWeighting = 1, truePeak = 1, winSize = 1024, hopSize = 512, maxWinSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), kWeighting, truePeak, winSize, hopSize, maxWinSize); + } + + init {arg ...theInputs; + inputs = theInputs; + ^this.initOutputs(2,rate); + } + + checkInputs { + if(inputs.at(5).rate != 'scalar') { + ^(": maxWinSize cannot be modulated."); + }; + ^this.checkValidInputs; + } +} diff --git a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp new file mode 100644 index 0000000..59c3c4b --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp @@ -0,0 +1,116 @@ +TITLE:: FluidBufLoudness +SUMMARY:: A Selection of Pitch Descriptors on a Buffer +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile + + +DESCRIPTION:: +This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. It is part of the 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 multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. Each sample represents a value, which is every hopSize. Its sampling rate is sourceSR / hopSize. + +CLASSMETHODS:: + +METHOD:: process + This is the method that calls for the pitch descriptor to be calculated on a given source buffer. + +ARGUMENT:: server + The server on which the buffers to be processed are allocated. + +ARGUMENT:: source + The index of the buffer to use as the source material to be pitch-tracked. The different channels of multichannel buffers will be processing sequentially. + +ARGUMENT:: startFrame + Where in the srcBuf should the process start, in sample. + +ARGUMENT:: numFrames + How many frames should be processed. + +ARGUMENT:: startChan + For multichannel srcBuf, which channel should be processed first. + +ARGUMENT:: numChans + For multichannel srcBuf, how many channel should be processed. + +ARGUMENT:: features + The destination buffer for the pitch descriptors. + +ARGUMENT:: kWeighting +(describe argument here) + +ARGUMENT:: truePeak +(describe argument here) + +ARGUMENT:: winSize +(describe argument here) + +ARGUMENT:: hopSize +(describe argument here) + +ARGUMENT:: action + A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. + +RETURNS:: + Nothing, as the destination buffer is declared in the function call. + +EXAMPLES:: + +code:: +// create a buffer with a short clicking sinusoidal burst (220Hz) starting at frame 8192 for 1024 frames +( +b = Buffer.sendCollection(s, (Array.fill(8192,{0}) ++ (Signal.sineFill(1203,[0,0,0,0,0,1],[0,0,0,0,0,0.5pi]).takeThese({|x,i|i>1023})) ++ Array.fill(8192,{0}))); +c = Buffer.new(s); +) + +// listen to the source and look at the buffer +b.play; b.plot; + d = Buffer.alloc(s,44100) +// run the process with basic parameters +( +Routine{ + t = Main.elapsedTime; + FluidBufLoudness.process(s, d, features: c); + (Main.elapsedTime - t).postln; +}.play +) + +// look at the analysis +c.plot(minval:-130, maxval:6) +// plot with a different range to appreciate the confidence: +c.plot + +// The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopesize, then multiply by 2 because of the channel interleaving +// here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30. +c.getn(30,10,{|x|x.postln}) + +// observe that the first frame is silent, as expected. The next frame's confidence is low-ish, because the window is half full (window of 1024, overlap of 512). Then a full window is analysed, with strong confidence. Then another half full window, then silence, as expected. +:: + +STRONG::A stereo buffer example.:: +CODE:: + +// load two very different files +( +b = Buffer.read(s,File.realpath(FluidBufLoudness.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav"); +c = Buffer.read(s,File.realpath(FluidBufLoudness.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +) + +// composite one on left one on right as test signals +FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) +b.play + +// create a buffer as destinations +c = Buffer.new(s); + +//run the process on them +( +Routine{ + t = Main.elapsedTime; + FluidBufLoudness.process(s, b, features: c, winSize: 17640, hopSize:4410); + (Main.elapsedTime - t).postln; +}.play +) + +// look at the buffer: [pitch,confidence] for left then [pitch,confidence] for right +c.plot(minval:-130, maxval:6) +:: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidLoudness.schelp b/release-packaging/HelpSource/Classes/FluidLoudness.schelp new file mode 100644 index 0000000..b1d3131 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidLoudness.schelp @@ -0,0 +1,100 @@ +TITLE:: FluidLoudness +SUMMARY:: A Selection of Pitch Descriptors in Real-Time +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Pitch + +DESCRIPTION:: +This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. 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 multichannel control steam with [pitch, confidence] values, which will be repeated if no change happens within the algorithm, i.e. when the hopSize is larger than the server's kr period. + +CLASSMETHODS:: + +METHOD:: kr + The audio rate in, control rate out version of the object. + +ARGUMENT:: in + The audio to be processed. + +ARGUMENT:: kWeighting +(describe argument here) + +ARGUMENT:: truePeak +(describe argument here) + +ARGUMENT:: winSize +(describe argument here) + +ARGUMENT:: hopSize +(describe argument here) + +ARGUMENT:: maxWinSize +(describe argument here) + +RETURNS:: + A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is winSize. + + +EXAMPLES:: + + +code:: +//create a monitoring bus for the descriptors +b = Bus.new(\control,0,2); + +//create a monitoring window for the values +( +w = Window("Loudness Monitor", Rect(10, 10, 220, 65)).front; + +c = Array.fill(2, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c[0].string = ("Loudness: "); +c[1].string = ("Peak: "); + +a = Array.fill(2, {arg i; + StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); +}); +) + +//routine to update the parameters +( +r = Routine { + { + + b.get({ arg val; + { + if(w.isClosed.not) { + val.do({arg item,index; + a[index].string = item.round(0.01)}) + } + }.defer + }); + + 0.1.wait; + }.loop + +}.play +) + +//test signals, all in one synth +( +x = { + arg freq=220, type = 0, noise = 0; + var source = PinkNoise.ar(noise) + Select.ar(type,[SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1), Mix.new(Array.fill(8, {arg i; SinOsc.ar(LFNoise1.kr(0.1.rand,10,220*(i+1)),mul:(i+1).reciprocal * 0.1)}))]); + Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640)); + source.dup; +}.play; +) + +// the built-in is slightly better on pure sinewaves +x.set(\freq, 440) + +// adding harmonics, by changing to triangle (1), saw (2) or square (3) shows that spectral algo are more resilient when signal are richer +x.set(\type, 1) +x.set(\type, 2) +x.set(\type, 3) + +// adding noise shows the comparative sturdiness of the spectral pitch tracker +x.set(\noise, 0.05) + +//if latency is no issue, getting a higher winSize will stabilise the algorithm even more +:: From 97462169f54dc0649a022f33cc0d84cb11c388a0 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 17 May 2019 13:47:47 +0100 Subject: [PATCH 30/50] updated helpfile --- release-packaging/HelpSource/Classes/FluidLoudness.schelp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidLoudness.schelp b/release-packaging/HelpSource/Classes/FluidLoudness.schelp index b1d3131..3264b79 100644 --- a/release-packaging/HelpSource/Classes/FluidLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidLoudness.schelp @@ -69,7 +69,7 @@ r = Routine { }.defer }); - 0.1.wait; + 0.01.wait; }.loop }.play @@ -80,7 +80,8 @@ r = Routine { x = { arg freq=220, type = 0, noise = 0; var source = PinkNoise.ar(noise) + Select.ar(type,[SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1), Mix.new(Array.fill(8, {arg i; SinOsc.ar(LFNoise1.kr(0.1.rand,10,220*(i+1)),mul:(i+1).reciprocal * 0.1)}))]); - Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640)); + // Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640)); + Out.kr(b, FluidLoudness.kr(source)); source.dup; }.play; ) From d875380c79e925cf590209549b3a73e7e2cd1293 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 17 May 2019 15:40:24 +0100 Subject: [PATCH 31/50] loudness: more troubleshooting code built-in --- .../HelpSource/Classes/FluidLoudness.schelp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidLoudness.schelp b/release-packaging/HelpSource/Classes/FluidLoudness.schelp index 3264b79..28bc3c0 100644 --- a/release-packaging/HelpSource/Classes/FluidLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidLoudness.schelp @@ -40,17 +40,19 @@ EXAMPLES:: code:: //create a monitoring bus for the descriptors -b = Bus.new(\control,0,2); +b = Bus.new(\control,0,4); //create a monitoring window for the values ( -w = Window("Loudness Monitor", Rect(10, 10, 220, 65)).front; +w = Window("Loudness Monitor", Rect(10, 10, 220, 115)).front; -c = Array.fill(2, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c = Array.fill(4, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); c[0].string = ("Loudness: "); c[1].string = ("Peak: "); +c[2].string = ("Loudness: "); +c[3].string = ("Peak: "); -a = Array.fill(2, {arg i; +a = Array.fill(4, {arg i; StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); }); ) @@ -78,10 +80,10 @@ r = Routine { //test signals, all in one synth ( x = { - arg freq=220, type = 0, noise = 0; - var source = PinkNoise.ar(noise) + Select.ar(type,[SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1), Mix.new(Array.fill(8, {arg i; SinOsc.ar(LFNoise1.kr(0.1.rand,10,220*(i+1)),mul:(i+1).reciprocal * 0.1)}))]); - // Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640)); - Out.kr(b, FluidLoudness.kr(source)); + arg freq=220, type = 1, noise = 0; + var source = PinkNoise.ar(noise) + Select.ar(type,[DC.ar(),SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1), Mix.new(Array.fill(8, {arg i; SinOsc.ar(LFNoise1.kr(0.1.rand,10,220*(i+1)),mul:(i+1).reciprocal * 0.1)}))]); + Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640) ++ FluidLoudness.kr(source,winSize:132300,hopSize:4410,maxWinSize:132300)); + // Out.kr(b, FluidLoudness.kr(source)); source.dup; }.play; ) From a55c423fd9c580d482894cccc8ac12d049d0c7b0 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sat, 18 May 2019 14:30:31 +0100 Subject: [PATCH 32/50] bufstat help + didactic section completed --- .../HelpSource/Classes/FluidBufStats.schelp | 61 +++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index 090e6d3..8d2aa67 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -1,14 +1,13 @@ TITLE:: FluidBufStats -SUMMARY:: Buffer-Based Novelty-Based Slicer +SUMMARY:: Computing Statistics on Buffers as Series. CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer RELATED:: Guides/FluCoMa, Guides/FluidDecomposition DESCRIPTION:: -This class implements a non-real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European 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. +This class implements non-real-time statistical analysis on buffer channels. Typically, a buffer would hold various time series (i.e. descriptors over time), and link::Classes/FluidBufStats:: allows this series to be described statistically. It is part of the 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 returns a buffer where each channel of the STRONG::source:: buffer has been reduced to 7 statistics: mean, standard deviation, skewness, kurtosis, followed by 3 percentiles, by default the minimum value, the median, and the maximum value. Moreover, it is possible to request the same 7 stats to be applied to derivative of the input. These are useful to describe statistically the rate of change of the time series. The STRONG::stats:: buffer will grow accordingly, yielding the seven same statistical description of the n requested derivative. Therefore, the STRONG::stats:: buffer will have as many channel as the input buffer, and as many frames as 7 times the requested STRONG::numDerivatives::. CLASSMETHODS:: @@ -22,49 +21,77 @@ ARGUMENT:: source The index of the buffer to use as the source material to be processed. The different channels of multichannel buffers will be considered independently as time series. ARGUMENT:: stats -(describe argument here) + The index of the buffer to write the statistics to. Each channel is the fruit of the statistical computations on the same channel number of the source buffer. -ARGUMENT:: numDerivatves -(describe argument here) +ARGUMENT:: numDerivatives + The number of derivatives of the original time series for the statistic to be computed on. By default, none are computed. This will influence the number of frames the stats buffer will have. ARGUMENT:: low -(describe argument here) + The rank requested for the first percentile value. By default, it is percentile 0.0, which is the minimum of the given channel of the source buffer. ARGUMENT:: middle -(describe argument here) + The rank requested for the second percentile value. By default, it is percentile 50.0, which is the median of the given channel of the source buffer. ARGUMENT:: high -(describe argument here) + The rank requested for the third percentile value. By default, it is percentile 100.0, which is the maximum of the given channel of the source buffer. ARGUMENT:: 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. + A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed stats as an argument. RETURNS:: Nothing, as the destination buffer is declared in the function call. EXAMPLES:: +STRONG::A didactic example:: + CODE:: // make a buffer of known lenght -b = Buffer.alloc(s,7); +b = Buffer.alloc(s,101); -// add known values - b.setn(0, Array.fill(7,{|i|i / 6})); +// add known values - here, a ramp up +b.setn(0, Array.fill(101,{|i|i / 100})); // create a new buffer as destinations c = Buffer.new(s); //run the process on them ( -// with basic params Routine{ t = Main.elapsedTime; - FluidBufStats.process(s,b, stats: c); + FluidBufStats.process(s,b, c, 1); (Main.elapsedTime - t).postln; }.play ) -// list the indicies of detected attacks - the two input channels have been summed +// list the statistics. The first seven are for the source buffer values themselves, the last seven for the first derivative of the source buffer. c.getn(0,c.numFrames,{|item|item.postln;}) + +// replace the source values by a ramp down +b.setn(0, Array.fill(101,{|i| 1 - (i / 100)})); + +// run the process and read the values +FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); + +// replace the source values by halfsine +b.setn(0, Array.fill(101,{|i| (i * pi/ 100).sin})); +b.plot + +// run the process and read the values +FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); + +// replace the source values by partial halfsine +b.setn(0, Array.fill(101,{|i| (i * pi/ 50).sin.max(0)})); +b.plot + +// run the process and read the values +FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); + +// replace the source values by positive white noise +b.setn(0, Array.fill(101,{1.0.rand})); +b.plot + +// run the process and read the values +FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); :: From 8b8f82efe164382995829b35683296b72682f5c6 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sat, 18 May 2019 15:39:16 +0100 Subject: [PATCH 33/50] bufstats: added buffer boundaries to the source and edited the name of one parameter --- release-packaging/Classes/FluidBufStats.sc | 4 +-- .../HelpSource/Classes/FluidBufStats.schelp | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/release-packaging/Classes/FluidBufStats.sc b/release-packaging/Classes/FluidBufStats.sc index 806b62e..8a838c3 100644 --- a/release-packaging/Classes/FluidBufStats.sc +++ b/release-packaging/Classes/FluidBufStats.sc @@ -1,5 +1,5 @@ FluidBufStats{ - *process { arg server, source, stats, numDerivatives = 0, low = 0, middle = 50, high = 100, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, action; source = source.asUGenInput; stats = stats.asUGenInput; @@ -10,7 +10,7 @@ FluidBufStats{ server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufStats, source, stats, numDerivatives, low, middle, high); + server.sendMsg(\cmd, \BufStats, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high); server.sync; stats = server.cachedBufferAt(stats); stats.updateInfo; server.sync; action.value(stats); diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index 8d2aa67..a03ee25 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -7,7 +7,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition DESCRIPTION:: This class implements non-real-time statistical analysis on buffer channels. Typically, a buffer would hold various time series (i.e. descriptors over time), and link::Classes/FluidBufStats:: allows this series to be described statistically. It is part of the 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 returns a buffer where each channel of the STRONG::source:: buffer has been reduced to 7 statistics: mean, standard deviation, skewness, kurtosis, followed by 3 percentiles, by default the minimum value, the median, and the maximum value. Moreover, it is possible to request the same 7 stats to be applied to derivative of the input. These are useful to describe statistically the rate of change of the time series. The STRONG::stats:: buffer will grow accordingly, yielding the seven same statistical description of the n requested derivative. Therefore, the STRONG::stats:: buffer will have as many channel as the input buffer, and as many frames as 7 times the requested STRONG::numDerivatives::. +The process returns a buffer where each channel of the STRONG::source:: buffer has been reduced to 7 statistics: mean, standard deviation, skewness, kurtosis, followed by 3 percentiles, by default the minimum value, the median, and the maximum value. Moreover, it is possible to request the same 7 stats to be applied to derivative of the input. These are useful to describe statistically the rate of change of the time series. The STRONG::stats:: buffer will grow accordingly, yielding the seven same statistical description of the n requested derivatives. Therefore, the STRONG::stats:: buffer will have as many channel as the input buffer, and as many frames as 7 times the requested STRONG::numDerivs::. CLASSMETHODS:: @@ -20,10 +20,22 @@ ARGUMENT:: server ARGUMENT:: source The index of the buffer to use as the source material to be processed. The different channels of multichannel buffers will be considered independently as time series. +ARGUMENT:: startFrame + The starting point (in samples) from which to copy in the source buffer. + +ARGUMENT:: numFrames + The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer. + +ARGUMENT:: startChan + The first channel from which to copy in the source buffer. + +ARGUMENT:: numChans + The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel. + ARGUMENT:: stats The index of the buffer to write the statistics to. Each channel is the fruit of the statistical computations on the same channel number of the source buffer. -ARGUMENT:: numDerivatives +ARGUMENT:: numDerivs The number of derivatives of the original time series for the statistic to be computed on. By default, none are computed. This will influence the number of frames the stats buffer will have. ARGUMENT:: low @@ -60,7 +72,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufStats.process(s,b, c, 1); + FluidBufStats.process(s, b, stats:c, numDerivatives:1); (Main.elapsedTime - t).postln; }.play ) @@ -72,26 +84,26 @@ c.getn(0,c.numFrames,{|item|item.postln;}) b.setn(0, Array.fill(101,{|i| 1 - (i / 100)})); // run the process and read the values -FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); // replace the source values by halfsine b.setn(0, Array.fill(101,{|i| (i * pi/ 100).sin})); b.plot // run the process and read the values -FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); // replace the source values by partial halfsine b.setn(0, Array.fill(101,{|i| (i * pi/ 50).sin.max(0)})); b.plot // run the process and read the values -FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); // replace the source values by positive white noise b.setn(0, Array.fill(101,{1.0.rand})); b.plot // run the process and read the values -FluidBufStats.process(s,b, c, 1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); :: From ab7726bd58c883c30949fcc3a29c8c7abc3119f7 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sat, 18 May 2019 20:18:56 +0100 Subject: [PATCH 34/50] bufstats help is finished for now (todo: translate the musical example) --- .../HelpSource/Classes/FluidBufStats.schelp | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index a03ee25..207be64 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -72,7 +72,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufStats.process(s, b, stats:c, numDerivatives:1); + FluidBufStats.process(s, b, stats:c, numDerivs:1); (Main.elapsedTime - t).postln; }.play ) @@ -84,26 +84,56 @@ c.getn(0,c.numFrames,{|item|item.postln;}) b.setn(0, Array.fill(101,{|i| 1 - (i / 100)})); // run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); // replace the source values by halfsine b.setn(0, Array.fill(101,{|i| (i * pi/ 100).sin})); b.plot // run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); // replace the source values by partial halfsine b.setn(0, Array.fill(101,{|i| (i * pi/ 50).sin.max(0)})); b.plot // run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); // replace the source values by positive white noise b.setn(0, Array.fill(101,{1.0.rand})); b.plot // run the process and read the values -FluidBufStats.process(s, b, stats:c, numDerivatives:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})}); +:: + +STRONG::A musical example:: + +CODE:: +// todo: port the Max one +:: + + +STRONG::Stereo Input Behaviour:: + +CODE:: +// make a buffer of known lenght +b = Buffer.alloc(s,101,2); + +// add known values - here, a ramp up on the left and negative random values on the right +b.setn(0, Array.fill(101,{|i|[i / 100,-1.0.rand]}).flat); + +// plot to confirm +b.plot + +// create a new buffer as destinations +c = Buffer.new(s); + +// run the stats and send back the values +FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames * c.numChannels,{|item|d = item; d.postln})}); + +//looking at the result is not easy to grasp, since it is interleaved: first number is mean of L, second is mean of R, third is stddev of L, fourth is stddev or R +//this will make it tidier - the first value of each line is Left, the second is Right +d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln}) :: From ded4b69f2912d99b57db7f7cc173b754d33b2ead Mon Sep 17 00:00:00 2001 From: Alex Harker Date: Sat, 18 May 2019 21:59:15 +0100 Subject: [PATCH 35/50] Typo in CMake error message --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 206fee1..c2c1c27 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ message(${FLUID_ABS_PATH}) # endif() if (NOT (EXISTS "${FLUID_ABS_PATH}/build/fluid_decomposition-exports.cmake")) - message(FATAL_ERROR "Can't find the fluid_decomposition CMake targets file at ${FLUID_ABS_PATH}/build/fluid_decomposition-expors.cmake. Please go to ${FLUID_ABS_PATH}/build and run CMake") + message(FATAL_ERROR "Can't find the fluid_decomposition CMake targets file at ${FLUID_ABS_PATH}/build/fluid_decomposition-exports.cmake. Please go to ${FLUID_ABS_PATH}/build and run CMake") endif() if (NOT (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/AudioFiles/)) From 5b146fdbc3b67dab586e511215e1e5ccb8beb0ab Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 19 May 2019 13:26:35 +0100 Subject: [PATCH 36/50] (buf)ampsplice objects, classes, and help skeleton --- release-packaging/Classes/FluidAmpSlice.sc | 11 +++ release-packaging/Classes/FluidBufAmpSlice.sc | 21 ++++ .../HelpSource/Classes/FluidAmpSlice.schelp | 99 +++++++++++++++++++ src/FluidAmpSlice/CMakeLists.txt | 20 ++++ src/FluidAmpSlice/FluidAmpSlice.cpp | 14 +++ src/FluidBufAmpSlice/CMakeLists.txt | 20 ++++ src/FluidBufAmpSlice/FluidBufAmpSlice.cpp | 15 +++ 7 files changed, 200 insertions(+) create mode 100644 release-packaging/Classes/FluidAmpSlice.sc create mode 100644 release-packaging/Classes/FluidBufAmpSlice.sc create mode 100644 release-packaging/HelpSource/Classes/FluidAmpSlice.schelp create mode 100644 src/FluidAmpSlice/CMakeLists.txt create mode 100644 src/FluidAmpSlice/FluidAmpSlice.cpp create mode 100644 src/FluidBufAmpSlice/CMakeLists.txt create mode 100644 src/FluidBufAmpSlice/FluidBufAmpSlice.cpp diff --git a/release-packaging/Classes/FluidAmpSlice.sc b/release-packaging/Classes/FluidAmpSlice.sc new file mode 100644 index 0000000..73d9e38 --- /dev/null +++ b/release-packaging/Classes/FluidAmpSlice.sc @@ -0,0 +1,11 @@ +FluidAmpSlice : UGen { + *ar { arg in = 0, absRampUp = 10, absRampDown = 10, absThreshOn = -40, absThreshOff = -40, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = -144, relThreshOff = -144, highPassFreq = 250, maxSize = 88200, outputType = 0; + ^this.multiNew('audio', in.asAudioRateInput(this), absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, outputType) + } + checkInputs { + if(inputs.at(16).rate != 'scalar') { + ^(": maxSize cannot be modulated."); + }; + ^this.checkValidInputs; + } +} diff --git a/release-packaging/Classes/FluidBufAmpSlice.sc b/release-packaging/Classes/FluidBufAmpSlice.sc new file mode 100644 index 0000000..568c308 --- /dev/null +++ b/release-packaging/Classes/FluidBufAmpSlice.sc @@ -0,0 +1,21 @@ +FluidBufAmpSlice{ + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -40, absThreshOff = -40, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = -144, relThreshOff = -144, highPassFreq = 250, outputType = 0, action; + + var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); + + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; + + server = server ? Server.default; + + forkIfNeeded{ + server.sendMsg(\cmd, \BufAmpSlice, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, outputType); + server.sync; + indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; + action.value(indices); + }; + } +} diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp new file mode 100644 index 0000000..93db1b7 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp @@ -0,0 +1,99 @@ +TITLE:: FluidAmpSlice +SUMMARY:: Amplitude-based Slicer +CATEGORIES:: Libraries>FluidDecomposition +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition + +DESCRIPTION:: +This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute and relative amplitude changes as onsets and offsets. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: + +FluidAmpSlice is based on two envelop followers on a highpassed version of the signal: one absolute, and one relative. Each have features that will interact, including independent Schmidt triggers and state-aware time contraints. The example code below is unfolding the various possibilites in order of complexity. + +The process will return an audio steam with square envelopes around detected slices the different slices, where 1s means in slice and 0s mean in silence. + +CLASSMETHODS:: + +METHOD:: ar + The audio rate version of the object. + +ARGUMENT:: in + The audio to be processed. + +ARGUMENT:: absRampUp + The number of samples the absolute envelope follower will take to reach the next value when raising. + +ARGUMENT:: absRampDown + The number of samples the absolute envelope follower will take to reach the next value when falling. + +ARGUMENT:: absThreshOn + The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state. + +ARGUMENT:: absThreshOff + The threshold in dB of the absolute envelope follower to trigger an offset, , aka to go ON when in OFF state. + +ARGUMENT:: minSliceLength + The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. + +ARGUMENT:: minSilenceLength + The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. + +ARGUMENT:: minLengthAbove + The length in samples that the absolute envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. + +ARGUMENT:: minLengthBelow + The length in samples that the absolute envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. + +ARGUMENT:: lookBack + The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm. + +ARGUMENT:: lookAhead + The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm. + +ARGUMENT:: relRampUp + The number of samples the relative envelope follower will take to reach the next value when raising. Typically, this will be faster than absRampUp. + +ARGUMENT:: relRampDown + The number of samples the relative envelope follower will take to reach the next value when falling. Typically, this will be faster than absRampDown. + +ARGUMENT:: relThreshOn + The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers. + +ARGUMENT:: relThreshOff + The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again. + +ARGUMENT:: highPassFreq + The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. + +ARGUMENT:: maxSize + How large can the buffer be for time-critical conditions, by allocating memory at instantiation time. This cannot be modulated. + +ARGUMENT:: outputType +(describe argument here) + +RETURNS:: + An audio stream with square envelopes around the slices. The latency between the input and the output is STRONG::max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead))::. + +EXAMPLES:: + +code:: +//basic tests: highPass sanity +( + {var env, source = SinOsc.ar(320,0,0.5); + env = FluidAmpSlice.ar(source,highPassFreq:2000, outputType:1); + [source, env] + }.plot(0.03); +) +//basic tests: absRampUp-Down sanity +( + {var env, source = SinOsc.ar(320,0,0.5); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:1000, outputType:2); + [source.abs, env] + }.plot(0.03); +) +//basic tests: absThresh sanity +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:1000, outputType:0, relThreshOn:-144, relThreshOff: -200); + [source, env] + }.plot(0.05); +) +:: diff --git a/src/FluidAmpSlice/CMakeLists.txt b/src/FluidAmpSlice/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidAmpSlice/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidAmpSlice/FluidAmpSlice.cpp b/src/FluidAmpSlice/FluidAmpSlice.cpp new file mode 100644 index 0000000..4a3abe0 --- /dev/null +++ b/src/FluidAmpSlice/FluidAmpSlice.cpp @@ -0,0 +1,14 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidAmpSlice", ft); +} diff --git a/src/FluidBufAmpSlice/CMakeLists.txt b/src/FluidBufAmpSlice/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidBufAmpSlice/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufAmpSlice/FluidBufAmpSlice.cpp b/src/FluidBufAmpSlice/FluidBufAmpSlice.cpp new file mode 100644 index 0000000..a861cfd --- /dev/null +++ b/src/FluidBufAmpSlice/FluidBufAmpSlice.cpp @@ -0,0 +1,15 @@ + +// FD_BufHPSS, an NRT buffer HPSS Processor +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(OfflineFluidDecompositionUGens) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("BufAmpSlice", ft); +} From 623341c50c019e56ef5761677f1510804555416b Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 19 May 2019 14:04:04 +0100 Subject: [PATCH 37/50] AmpSlice: More sanity tests --- .../HelpSource/Classes/FluidAmpSlice.schelp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp index 93db1b7..2b5f92f 100644 --- a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp @@ -77,10 +77,10 @@ EXAMPLES:: code:: //basic tests: highPass sanity ( - {var env, source = SinOsc.ar(320,0,0.5); - env = FluidAmpSlice.ar(source,highPassFreq:2000, outputType:1); - [source, env] - }.plot(0.03); +{var env, source = SinOsc.ar(320,0,0.5); + env = FluidAmpSlice.ar(source,highPassFreq:2000, outputType:1); + [source, env] +}.plot(0.03); ) //basic tests: absRampUp-Down sanity ( @@ -91,7 +91,7 @@ code:: ) //basic tests: absThresh sanity ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:1000, outputType:0, relThreshOn:-144, relThreshOff: -200); [source, env] }.plot(0.05); From cfafc629ba00591342941bc6c2c05d1479f250ac Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 19 May 2019 15:46:53 +0100 Subject: [PATCH 38/50] a typo in most helpfiles (hopeSize is large at the moment) --- release-packaging/HelpSource/Classes/FluidBufNMF.schelp | 2 +- .../HelpSource/Classes/FluidBufNoveltySlice.schelp | 2 +- .../HelpSource/Classes/FluidBufOnsetSlice.schelp | 2 +- release-packaging/HelpSource/Classes/FluidBufPitch.schelp | 6 +++--- release-packaging/HelpSource/Classes/FluidBufSines.schelp | 2 +- .../HelpSource/Classes/FluidBufSpectralShape.schelp | 2 +- release-packaging/HelpSource/Classes/FluidHPSS.schelp | 5 +---- release-packaging/HelpSource/Classes/FluidNMFFilter.schelp | 2 +- release-packaging/HelpSource/Classes/FluidNMFMatch.schelp | 2 +- release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp | 2 +- release-packaging/HelpSource/Classes/FluidPitch.schelp | 2 +- release-packaging/HelpSource/Classes/FluidSines.schelp | 2 +- .../HelpSource/Classes/FluidSpectralShape.schelp | 2 +- 13 files changed, 15 insertions(+), 18 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index 06b191e..ae37561 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -91,7 +91,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp index 301c1b0..a0d7e6c 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp @@ -49,7 +49,7 @@ ARGUMENT:: winSize The window size. As novelty estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hope size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. 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. diff --git a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp index 9b3b183..83e5041 100644 --- a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp @@ -65,7 +65,7 @@ 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). + The window hop size. As spectral differencing relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 06e9484..17d1d6b 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -47,7 +47,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. 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. @@ -84,7 +84,7 @@ c.plot(minval:0, maxval:400) // plot with a different range to appreciate the confidence: c.plot(minval:0, maxval:1) -// The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopesize, then multiply by 2 because of the channel interleaving +// The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopSize, then multiply by 2 because of the channel interleaving // here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30. c.getn(30,10,{|x|x.postln}) @@ -174,4 +174,4 @@ Routine({ }) }).play; ) -:: \ No newline at end of file +:: diff --git a/release-packaging/HelpSource/Classes/FluidBufSines.schelp b/release-packaging/HelpSource/Classes/FluidBufSines.schelp index 53c8237..1ef0f9e 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSines.schelp @@ -61,7 +61,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. 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. diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index 6e142e3..78250cc 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -55,7 +55,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. 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. diff --git a/release-packaging/HelpSource/Classes/FluidHPSS.schelp b/release-packaging/HelpSource/Classes/FluidHPSS.schelp index c9b0e2d..9fc8aa3 100644 --- a/release-packaging/HelpSource/Classes/FluidHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidHPSS.schelp @@ -70,7 +70,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. @@ -84,17 +84,14 @@ ARGUMENT::maxHarmFilterSize ARGUMENT:: maxPercFilterSize How large can the percussive filter be modulated to (percFilterSize), by allocating memory at instantiation time. This cannot be modulated. - RETURNS:: An array of three audio streams: [0] is the harmonic part extracted, [1] is the percussive part extracted, [2] is the rest. The latency between the input and the output is ((harmFilterSize - 1) * hopSize) + 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 maskingMode parameter provides different approaches to combinging estimates and producing masks. Some settings (especially in modes 1 & 2) will provide better separation but with more artefacts. These can, in principle, be ameliorated by applying smoothing filters to the masks before transforming back to the time-domain (not yet implemented). - EXAMPLES:: CODE:: diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index 958bdb5..5a34698 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -38,7 +38,7 @@ ARGUMENT:: winSize The number of samples that are analysed at a time. A lower number yields greater temporal resolution, at the expense of spectral resoultion, and vice-versa. ARGUMENT:: hopSize - The window hope size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). ARGUMENT:: fftSize The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. diff --git a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp index 8067809..7953d7b 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp @@ -38,7 +38,7 @@ ARGUMENT:: winSize The number of samples that are analysed at a time. A lower number yields greater temporal resolution, at the expense of spectral resoultion, and vice-versa. ARGUMENT:: hopSize - The window hope size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). ARGUMENT:: fftSize The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. diff --git a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp index a99ea2b..29ae2ee 100644 --- a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp @@ -48,7 +48,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. diff --git a/release-packaging/HelpSource/Classes/FluidPitch.schelp b/release-packaging/HelpSource/Classes/FluidPitch.schelp index 44b1c52..9cb1f90 100644 --- a/release-packaging/HelpSource/Classes/FluidPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidPitch.schelp @@ -28,7 +28,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. diff --git a/release-packaging/HelpSource/Classes/FluidSines.schelp b/release-packaging/HelpSource/Classes/FluidSines.schelp index 69851dc..83010a7 100644 --- a/release-packaging/HelpSource/Classes/FluidSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidSines.schelp @@ -40,7 +40,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp index 02b0821..3977f16 100644 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp @@ -36,7 +36,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. From 1a6cf05e5a942569f6714bbb831cc8be4175dddb Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 19 May 2019 16:22:05 +0100 Subject: [PATCH 39/50] (buf)loudness help file finished --- .../Classes/FluidBufLoudness.schelp | 41 ++++++----- .../HelpSource/Classes/FluidLoudness.schelp | 72 +++++++++++-------- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp index 59c3c4b..3b41cd7 100644 --- a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp @@ -1,24 +1,24 @@ TITLE:: FluidBufLoudness -SUMMARY:: A Selection of Pitch Descriptors on a Buffer +SUMMARY:: A Loudness and True-Peak Descriptor on a Buffer CATEGORIES:: Libraries>FluidDecomposition -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition DESCRIPTION:: -This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. 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 two loudness descriptors, computing the true peak of the signal as well as applying the filters proposed by broadcasting standards to emulate the perception of amplitude. It is part of the 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 multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. Each sample represents a value, which is every hopSize. Its sampling rate is sourceSR / hopSize. +The process will return a multichannel buffer with two channels per input channel, one for loudness and one for the true peak value of the frame, both in dBfs. More information on broadcasting standardisation of loudness measurement is available at the reference page FOOTNOTE::https://tech.ebu.ch/docs/tech/tech3341.pdf:: and in more musician-friendly explantions here FOOTNOTE::http://designingsound.org/2013/02/06/loudness-and-metering-part-1/::. Each sample represents a value, which is every hopSize. Its sampling rate is STRONG::sourceSR / hopSize::. CLASSMETHODS:: METHOD:: process - This is the method that calls for the pitch descriptor to be calculated on a given source buffer. + This is the method that calls for the loudness descriptor to be calculated on a given source buffer. ARGUMENT:: server The server on which the buffers to be processed are allocated. ARGUMENT:: source - The index of the buffer to use as the source material to be pitch-tracked. The different channels of multichannel buffers will be processing sequentially. + The index of the buffer to use as the source material to be described. The different channels of multichannel buffers will be processing sequentially. ARGUMENT:: startFrame Where in the srcBuf should the process start, in sample. @@ -33,19 +33,19 @@ ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. ARGUMENT:: features - The destination buffer for the pitch descriptors. + The destination buffer for the loudness descriptors. ARGUMENT:: kWeighting -(describe argument here) + A flag to switch the perceptual model of loudness. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple RMS of the frame. ARGUMENT:: truePeak -(describe argument here) + A flag to switch the computation of TruePeak. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple absolute peak of the frame. ARGUMENT:: winSize -(describe argument here) + The size of the window on which the computation is done. By default 1024 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. ARGUMENT:: hopSize -(describe argument here) + How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. @@ -64,26 +64,25 @@ c = Buffer.new(s); // listen to the source and look at the buffer b.play; b.plot; - d = Buffer.alloc(s,44100) + // run the process with basic parameters ( Routine{ t = Main.elapsedTime; - FluidBufLoudness.process(s, d, features: c); + FluidBufLoudness.process(s, source:b, features: c); (Main.elapsedTime - t).postln; }.play ) // look at the analysis c.plot(minval:-130, maxval:6) -// plot with a different range to appreciate the confidence: -c.plot -// The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopesize, then multiply by 2 because of the channel interleaving +// The values are interleaved [loudness,truepeak] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopSize, then multiply by 2 because of the channel interleaving // here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30. + c.getn(30,10,{|x|x.postln}) -// observe that the first frame is silent, as expected. The next frame's confidence is low-ish, because the window is half full (window of 1024, overlap of 512). Then a full window is analysed, with strong confidence. Then another half full window, then silence, as expected. +// observe that the first frame is silent, as expected. We can appreciate the overshoot of TruePeak of a full range sinewave starting on the second sample (fourth item in the list). :: STRONG::A stereo buffer example.:: @@ -102,7 +101,7 @@ b.play // create a buffer as destinations c = Buffer.new(s); -//run the process on them +//run the process on them with EBU standard Instant Loudness of ( Routine{ t = Main.elapsedTime; @@ -111,6 +110,6 @@ Routine{ }.play ) -// look at the buffer: [pitch,confidence] for left then [pitch,confidence] for right -c.plot(minval:-130, maxval:6) -:: \ No newline at end of file +// look at the buffer: [loudness,truepeak] for left then [loudness,truepeak] for right +c.plot(minval:-40, maxval:0) +:: diff --git a/release-packaging/HelpSource/Classes/FluidLoudness.schelp b/release-packaging/HelpSource/Classes/FluidLoudness.schelp index 28bc3c0..75d574e 100644 --- a/release-packaging/HelpSource/Classes/FluidLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidLoudness.schelp @@ -1,12 +1,12 @@ TITLE:: FluidLoudness -SUMMARY:: A Selection of Pitch Descriptors in Real-Time +SUMMARY:: A Loudness and True-Peak Descriptor in Real-Time CATEGORIES:: Libraries>FluidDecomposition -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Pitch +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition DESCRIPTION:: -This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. 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 two loudness descriptors, computing the true peak of the signal as well as applying the filters proposed by broadcasting standards to emulate the perception of amplitude. It is part of the 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 multichannel control steam with [pitch, confidence] values, which will be repeated if no change happens within the algorithm, i.e. when the hopSize is larger than the server's kr period. +The process will return a multichannel control steam with [loudness, truepeak] values, both in dBfs, which will be repeated if no change happens within the algorithm, i.e. when the hopSize is larger than the server's kr period. More information on broadcasting standardisation of loudness measurement is available at the reference page FOOTNOTE::https://tech.ebu.ch/docs/tech/tech3341.pdf:: and in more musician-friendly explantions here FOOTNOTE::http://designingsound.org/2013/02/06/loudness-and-metering-part-1/::. CLASSMETHODS:: @@ -17,19 +17,19 @@ ARGUMENT:: in The audio to be processed. ARGUMENT:: kWeighting -(describe argument here) + A flag to switch the perceptual model of loudness. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple RMS of the frame. ARGUMENT:: truePeak -(describe argument here) + A flag to switch the computation of TruePeak. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple absolute peak of the frame. ARGUMENT:: winSize -(describe argument here) + The size of the window on which the computation is done. By default 1024 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. ARGUMENT:: hopSize -(describe argument here) + How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. ARGUMENT:: maxWinSize -(describe argument here) + How large can the winSize be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is winSize. @@ -40,19 +40,17 @@ EXAMPLES:: code:: //create a monitoring bus for the descriptors -b = Bus.new(\control,0,4); +b = Bus.new(\control,0,2); //create a monitoring window for the values ( -w = Window("Loudness Monitor", Rect(10, 10, 220, 115)).front; +w = Window("Loudness Monitor", Rect(10, 10, 220, 65)).front; -c = Array.fill(4, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); +c = Array.fill(2, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); c[0].string = ("Loudness: "); c[1].string = ("Peak: "); -c[2].string = ("Loudness: "); -c[3].string = ("Peak: "); -a = Array.fill(4, {arg i; +a = Array.fill(2, {arg i; StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); }); ) @@ -61,7 +59,6 @@ a = Array.fill(4, {arg i; ( r = Routine { { - b.get({ arg val; { if(w.isClosed.not) { @@ -70,34 +67,49 @@ r = Routine { } }.defer }); - - 0.01.wait; + 0.1.wait; }.loop - }.play ) -//test signals, all in one synth +//basic test, with default values +( +x = {var source = PinkNoise.ar(0.25); + Out.kr(b, FluidLoudness.kr(source)); + source.dup; +}.play; +) + +//free this +x.free + +//the EBU standard specifies that the window should be 400ms long, and update every 100ms, for instantaneous loudness. At SR=44100, this means the following settings. Various test signals are loaded. ( x = { arg freq=220, type = 1, noise = 0; - var source = PinkNoise.ar(noise) + Select.ar(type,[DC.ar(),SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1), Mix.new(Array.fill(8, {arg i; SinOsc.ar(LFNoise1.kr(0.1.rand,10,220*(i+1)),mul:(i+1).reciprocal * 0.1)}))]); - Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640) ++ FluidLoudness.kr(source,winSize:132300,hopSize:4410,maxWinSize:132300)); - // Out.kr(b, FluidLoudness.kr(source)); + var source = PinkNoise.ar(noise) + Select.ar(type,[DC.ar(),SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1)]); + Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640)); source.dup; }.play; ) - -// the built-in is slightly better on pure sinewaves +// change the various frequencies to see the impact of the filter for the loudness. The TruePeak is steady. x.set(\freq, 440) +x.set(\freq, 110) +x.set(\freq, 55) +x.set(\freq, 3000) +x.set(\freq, 9000) -// adding harmonics, by changing to triangle (1), saw (2) or square (3) shows that spectral algo are more resilient when signal are richer -x.set(\type, 1) +// adding harmonics, by changing to triangle (2), saw (3) or square (4) shows that spectral algo are more resilient when signal are richer x.set(\type, 2) x.set(\type, 3) +x.set(\type, 4) + +// adding noise shows its impact on loudness +x.set(\noise, 0.25) -// adding noise shows the comparative sturdiness of the spectral pitch tracker -x.set(\noise, 0.05) +// and removing the oscilator +x.set(\type, 0) -//if latency is no issue, getting a higher winSize will stabilise the algorithm even more +// and measuring silence +x.set(\noise, 0) :: From c4a4d9ecaa99117463b091b8c9512d4726c0d157 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 19 May 2019 21:53:49 +0100 Subject: [PATCH 40/50] (buf) melbands helpfile and demo (plus typo in ampslice) --- .../Classes/FluidBufMelBands.schelp | 44 +++------ .../HelpSource/Classes/FluidMelBands.schelp | 92 +++++++++++-------- .../Classes/FluidSpectralShape.schelp | 2 +- 3 files changed, 70 insertions(+), 68 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp index 8398ad4..3175ba8 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -1,29 +1,13 @@ TITLE:: FluidBufMelBands -SUMMARY:: Seven Spectral Shape Descriptors on a Buffer +SUMMARY:: A Perceptually Spread Spectral Contour Descriptor on a Buffer CATEGORIES:: Libraries>FluidDecomposition -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufMFCC DESCRIPTION:: -This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: -LIST:: -##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: - LIST:: - ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. - ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. - :: - ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. - ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. - ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. - - The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. -:: +This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the 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 multichannel buffer with the seven channels per input channel, each containing the 7 shapes. Each sample represents a value, which is every hopSize. +The process will return a single multichannel buffer of STRONG::numBands:: per input channel. Each frame represents a value, which is every hopSize. CLASSMETHODS:: @@ -52,19 +36,19 @@ ARGUMENT:: features The destination buffer for the 7 spectral features describing the spectral shape. ARGUMENT:: numBands -(describe argument here) + The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. It will decide how many channels are produce per channel of the source. ARGUMENT:: minFreq -(describe argument here) + The lower boundary of the lowest band of the model, in Hz. ARGUMENT:: maxFreq -(describe argument here) + The highest boundary of the highest band of the model, in Hz. 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 + The window size. As spectral description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. 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. @@ -80,7 +64,7 @@ EXAMPLES:: code:: // create some buffers ( -b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +b = Buffer.read(s,File.realpath(FluidBufMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); c = Buffer.new(s); ) @@ -88,7 +72,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMelBands.process(s, b, features: c,numBands:10); + FluidBufMelBands.process(s, b, features: c, numBands:10); (Main.elapsedTime - t).postln; }.play ) @@ -118,11 +102,11 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufMelBands.process(s, b, features: c, numBands:10); (Main.elapsedTime - t).postln; }.play ) -// look at the buffer: 7shapes for left, then 7 shapes for right -c.plot(minval:-25, maxval:150) +// look at the buffer: 10 bands for left, then 10 bands for right +c.plot(minval:0, maxval:100) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidMelBands.schelp b/release-packaging/HelpSource/Classes/FluidMelBands.schelp index e28773e..4e3ffbc 100644 --- a/release-packaging/HelpSource/Classes/FluidMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidMelBands.schelp @@ -1,28 +1,12 @@ TITLE:: FluidMelBands -SUMMARY:: Seven Spectral Shape Descriptors in Real-Time +SUMMARY:: A Perceptually Spread Spectral Contour Descriptor in Real-Time CATEGORIES:: Libraries>FluidDecomposition -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMFCC DESCRIPTION:: -This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: -LIST:: -##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: - LIST:: - ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. - ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. - :: - ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. - ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. - ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. - - The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. -:: +This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the 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 multichannel control steam with the seven values, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. +The process will return a multichannel control steam of size STRONG::maxNumBands::, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. CLASSMETHODS:: @@ -33,13 +17,13 @@ ARGUMENT:: in The audio to be processed. ARGUMENT:: numBands -(describe argument here) + The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. It is limited by the STRONG::maxNumBands:: parameter. When the number is smaller than the maximum, the output is zero-padded. ARGUMENT:: minFreq -(describe argument here) + The lower boundary of the lowest band of the model, in Hz. ARGUMENT:: maxFreq -(describe argument here) + The highest boundary of the highest band of the model, in Hz. ARGUMENT:: maxNumBands The maximum number of Mel bands that can be modelled. This sets the number of channels of the output, and therefore cannot be modulated. @@ -48,7 +32,7 @@ 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 ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. @@ -57,12 +41,11 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A 7-channel KR signal with the seven spectral shape descriptors. The latency is winSize. + A KR signal of STRONG::maxNumBands:: channels, giving the measure amplitudes for each band. The latency is winSize. EXAMPLES:: - code:: //create a monitoring bus for the descriptors b = Bus.new(\control,0,40); @@ -74,11 +57,10 @@ w = Window("Mel Bands Monitor", Rect(10, 10, 620, 320)).front; a = MultiSliderView(w,Rect(10, 10, 600, 300)).elasticMode_(1).isFilled_(1); ) -//run the wondow updating routine. +//run the window updating routine. ( r = Routine { { - b.get({ arg val; { if(w.isClosed.not) { @@ -88,18 +70,54 @@ r = Routine { }); 0.01.wait; }.loop - }.play ) //play a simple sound to observe the values ( - { - var source; - // source = SinOsc.ar(220,0,0.1); - source = BPF.ar(WhiteNoise.ar(), 330, 55/330); - Out.kr(b,FluidMelBands.kr(source,maxNumBands:40)); - source.dup; - }.play; +x = { + var source = SinOsc.ar(LFTri.kr(0.1).exprange(80,800),0,0.1); + Out.kr(b,FluidMelBands.kr(source,maxNumBands:40) / 50); + source.dup; +}.play; +) + +// free this source +x.free + +// load a more exciting one +c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav"); + +// analyse with parameters to be changed +( +x = {arg bands = 40, low = 20, high = 20000; + var source = PlayBuf.ar(1,c,loop:1); + Out.kr(b,FluidMelBands.kr(source, bands, low, high, 40) / 10); + source.dup; +}.play; ) -:: \ No newline at end of file + +// observe the number of bands. The unused ones at the top are not updated +x.set(\bands,20) + +// back to the full range +x.set(\bands,40) + +// focus all the bands on a mid range +x.set(\low,320, \high, 8000) + +// focusing on the low end shows the fft resolution issue. One could restart the analysis with a larger fft to show more precision +x.set(\low,20, \high, 160) + +// back to full range +x.set(\low,20, \high, 20000) + +// free everything +x.free;b.free;c.free;r.stop; +:: + +STRONG::A musical example:: + +CODE:: +// todo: port the Max one +:: diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp index 3977f16..5dc28c4 100644 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp @@ -74,7 +74,7 @@ a = Array.fill(7, {arg i; }); ) -//run the wondow updating routine. +//run the window updating routine. ( r = Routine { { From 3420ab1155e1ca0681fc28d21166b13c61437965 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 19 May 2019 23:04:23 +0100 Subject: [PATCH 41/50] (buf)mfccs help done, and typos in melbands sorted --- .../HelpSource/Classes/FluidBufMFCC.schelp | 51 ++-- .../Classes/FluidBufMelBands.schelp | 2 +- .../HelpSource/Classes/FluidMFCC.schelp | 222 ++++-------------- 3 files changed, 65 insertions(+), 210 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp index 3fc55c4..5c47825 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -1,29 +1,12 @@ TITLE:: FluidBufMFCC -SUMMARY:: Seven Spectral Shape Descriptors on a Buffer +SUMMARY:: Mel-Frequency Cepstral Coefficients as Spectral Descriptors on a Buffer CATEGORIES:: Libraries>FluidDecomposition -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile - +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufMelBands DESCRIPTION:: -This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: -LIST:: -##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: - LIST:: - ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. - ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. - :: - ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. - ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. - ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. - - The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. -:: +This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first decomposed in STRONG::numBands:: perceptually space bands, as in LINK::Classes/FluidMelBands::. It is then analysed in STRONG::numCoefs:: number of cepstral coefficients. It has the avantage to be amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: - The process will return a multichannel buffer with the seven channels per input channel, each containing the 7 shapes. Each sample represents a value, which is every hopSize. +The process will return a single multichannel buffer of STRONG::numCoefs:: per input channel. Each frame represents a value, which is every hopSize. CLASSMETHODS:: @@ -49,25 +32,25 @@ ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. ARGUMENT:: features - The destination buffer for the 7 spectral features describing the spectral shape. + The destination buffer for the numCoefs coefficients describing the spectral shape. ARGUMENT:: numCoefs -(describe argument here) + The number of cepstral coefficients to be outputed. It will decide how many channels are produce per channel of the source. ARGUMENT:: numBands -(describe argument here) + The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. ARGUMENT:: minFreq -(describe argument here) + The lower boundary of the lowest band of the model, in Hz. ARGUMENT:: maxFreq -(describe argument here) + The highest boundary of the highest band of the model, in Hz. 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 + The window size. As MFCC computation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. 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. @@ -83,7 +66,7 @@ EXAMPLES:: code:: // create some buffers ( -b = Buffer.read(s,File.realpath(FluidBufSpectralShape.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +b = Buffer.read(s,File.realpath(FluidBufMFCC.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); c = Buffer.new(s); ) @@ -91,14 +74,14 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufMFCC.process(s, b, features: c); (Main.elapsedTime - t).postln; }.play ) // listen to the source and look at the buffer b.play; -c.plot(minval:-5, maxval:250) +c.plot(separately:true) :: STRONG::A stereo buffer example.:: @@ -121,11 +104,11 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufMFCC.process(s, b, numCoefs:5, features: c); (Main.elapsedTime - t).postln; }.play ) -// look at the buffer: 7shapes for left, then 7 shapes for right -c.plot(minval:-25, maxval:150) +// look at the buffer: 5 coefs for left, then 5 coefs for right +c.plot(separately:true) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp index 3175ba8..4d95dc4 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -33,7 +33,7 @@ ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. ARGUMENT:: features - The destination buffer for the 7 spectral features describing the spectral shape. + The destination buffer for the STRONG::numBands:: amplitudes describing the spectral shape. ARGUMENT:: numBands The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. It will decide how many channels are produce per channel of the source. diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp index ece0a1d..05905e9 100644 --- a/release-packaging/HelpSource/Classes/FluidMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidMFCC.schelp @@ -1,28 +1,12 @@ TITLE:: FluidMFCC -SUMMARY:: Seven Spectral Shape Descriptors in Real-Time +SUMMARY:: Mel-Frequency Cepstral Coefficients as Spectral Descriptors in Real-Time CATEGORIES:: Libraries>FluidDecomposition -RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMelBands DESCRIPTION:: -This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the 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 descriptors are: -LIST:: -##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: - LIST:: - ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. - ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. - :: - ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. - ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. - ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. - - The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. -:: +This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first decomposed in STRONG::numBands:: perceptually space bands, as in LINK::Classes/FluidMelBands::. It is then analysed in STRONG::numCoefs:: number of cepstral coefficients. It has the avantage to be amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: - The process will return a multichannel control steam with the seven values, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. +The process will return a multichannel control steam of STRONG::maxNumCoefs::, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. CLASSMETHODS:: @@ -33,25 +17,25 @@ ARGUMENT:: in The audio to be processed. ARGUMENT:: numCoefs -(describe argument here) + The number of cepstral coefficients to be outputed. It is limited by the maxNumCoefs parameter. When the number is smaller than the maximum, the output is zero-padded. ARGUMENT:: numBands -(describe argument here) + The number of bands that will be perceptually equally distributed between minFreq and maxFreq to describe the spectral shape before it is converted to cepstral coefficients. ARGUMENT:: minFreq -(describe argument here) + The lower boundary of the lowest band of the model, in Hz. ARGUMENT:: maxFreq -(describe argument here) + The highest boundary of the highest band of the model, in Hz. ARGUMENT:: maxNumCoefs -(describe argument here) + The maximum number of cepstral coefficients that can be computed. This sets the number of channels of the output, and therefore cannot be modulated. 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 + The window size. As MFCC computation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hope size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. @@ -60,199 +44,87 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A 7-channel KR signal with the seven spectral shape descriptors. The latency is winSize. + A KR signal of STRONG::maxNumCoefs:: channels. The latency is winSize. EXAMPLES:: - code:: //create a monitoring bus for the descriptors -b = Bus.new(\control,0,7); +b = Bus.new(\control,0,13); //create a monitoring window for the values ( -w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; - -c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("Centroid: "); -c[1].string = ("Spread: "); -c[2].string = ("Skewness: "); -c[3].string = ("Kurtosis: "); -c[4].string = ("Rolloff: "); -c[5].string = ("Flatness: "); -c[6].string = ("Crest: "); - -a = Array.fill(7, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); +w = Window("MFCCs Monitor", Rect(10, 10, 420, 320)).front; +a = MultiSliderView(w,Rect(10, 10, 400, 300)).elasticMode_(1).isFilled_(1); ) -//run the wondow updating routine. +//run the window updating routine. ( r = Routine { { - b.get({ arg val; { if(w.isClosed.not) { - val.do({arg item,index; - a[index].string = item.round(0.01)}) + a.value = val; } }.defer }); - 0.01.wait; }.loop - }.play ) //play a simple sound to observe the values ( - { - var source; - source = BPF.ar(WhiteNoise.ar(), 330, 55/330); - Out.kr(b,FluidSpectralShape.kr(source)); - source.dup; - }.play; -) -:: - -STRONG::A commented tutorial on how each descriptor behaves with test signals: :: - -CODE:: -// as above, create a monitoring bus for the descriptors -b = Bus.new(\control,0,7); - -//again, create a monitoring window for the values -( -w = Window("Frequency Monitor", Rect(10, 10, 220, 190)).front; - -c = Array.fill(7, {arg i; StaticText(w, Rect(10, i * 25 + 10, 135, 20)).background_(Color.grey(0.7)).align_(\right)}); -c[0].string = ("Centroid: "); -c[1].string = ("Spread: "); -c[2].string = ("Skewness: "); -c[3].string = ("Kurtosis: "); -c[4].string = ("Rolloff: "); -c[5].string = ("Flatness: "); -c[6].string = ("Crest: "); - -a = Array.fill(7, {arg i; - StaticText(w, Rect(150, i * 25 + 10, 60, 20)).background_(Color.grey(0.7)).align_(\center); -}); -) - -// this time, update a little more slowly, and convert in Hz the 3 descriptors published in bins by the algorythm. -( -r = Routine { - { - - b.get({ arg val; - { - if(w.isClosed.not) { - val.do({arg item,index; - if ((index < 2) || (index == 4)) - { - a[index].string = (item * s.sampleRate / 1024).round(0.01); - } { - a[index].string = item.round(0.01); - }; - }) - } - }.defer - }); - - 0.2.wait; - }.loop - -}.play -) - -// first, a sine wave -( -x = { - arg freq=220; - var source; - source = SinOsc.ar(freq,mul:0.1); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); - source.dup; -}.play; -) - -// at 220, the centroid is on the frequency, the spread is narrow, but as wide as the FFT Hann window ripples, the skewness is high as we are low and therefore far left of the middle bin (aka half-Nyquist), the Kurtosis is incredibly high as we have a very peaky spectrum. The rolloff is slightly higher than the frequency, taking into account the FFT windowing ripples, the flatness is incredibly low, as we have one peak and not much else, and the crest is quite high, because most of the energy is in a few peaky bins. - -x.set(\freq, 440) - -// at 440, the skewness has changed (we are nearer the middle of the spectrogram) and the Kurtosis too, although it is still so high it is quite in the same order of magnitude. The rest is stable, as expected. - -x.set(\freq, 11000) - -// at 11kHz, kurtosis is still in the thousand, but skewness is almost null, as expected. - -x.free - -// second, broadband noise -( -x = { - arg type = 0; - var source; - source = Select.ar(type,[WhiteNoise.ar(0.1),PinkNoise.ar(0.1)]); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); +x = {arg type = 0; + var source = Select.ar(type,[SinOsc.ar(220),Saw.ar(220),Pulse.ar(220)]) * LFTri.kr(0.1).exprange(0.01,0.1); + Out.kr(b,FluidMFCC.kr(source,maxNumCoefs:13)); source.dup; }.play; ) -// white noise has a linear repartition of energy, so we would expect a centroid in the middle bin (aka half-Nyquist) with a spread covering the full range (+/- a quarter-Nyquist), with a skewness almost null since we are centered, and a very low Kurtosis since we are flat. The rolloff should be almost at Nyquist, the flatness as high as it gets, and the crest quite low. - +// change the wave types, observe the amplitude invariance of the descriptors, apart from the leftmost coefficient x.set(\type, 1) - -// pink noise has a drop of 3dB per octave across the spectrum, so we would, by comparison, expect a lower centroid, a slighly higher skewness and kurtosis, a lower rolloff, a slighly lower flatness and a higher crest for the larger low-end energy. - +x.set(\type, 2) +x.set(\type, 0) +// free this source x.free -// third, bands of noise +// load a more exciting one +c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav"); + +// analyse with parameters to be changed ( -x = { - arg type = 0; - var source, chain; - chain = FFT(LocalBuf(1024), WhiteNoise.ar(0.5)); - chain = chain.pvcollect(1024, {arg mag,phase;[mag,phase]},5,11,1); - source = Select.ar(type,[ - BPF.ar(BPF.ar(WhiteNoise.ar(0.5),330,0.666),330,0.666), - IFFT(chain)]); - Out.kr(b, VarLag.kr(FluidSpectralShape.kr(source),1024/s.sampleRate)); +x = {arg bands = 40, low = 20, high = 20000; + var source = PlayBuf.ar(1,c,loop:1); + Out.kr(b,FluidMelBands.kr(source, bands, low, high, 40) / 10); source.dup; }.play; ) -// a second-order bandpass filter on whitenoise, centred on 330Hz with one octave bandwidth, gives us a centroid quite high. This is due to the exponential behaviour of the filter, with a gentle slope. Observe the spectral analyser: +// observe the number of bands. The unused ones at the top are not updated +x.set(\bands,20) -s.freqscope - -// at first it seems quite centred, but then flip the argument FrqScl to lin(ear) and observe how high the spectrum goes. If we set it to a brickwall spectral filter tuned on the same frequencies: - -x.set(\type, 1) +// back to the full range +x.set(\bands,40) -// we have a much narrower register, and our centroid and spread, as well as the kurtosis and flatness, agrees with this reading. +// focus all the bands on a mid range +x.set(\low,320, \high, 8000) -x.free +// focusing on the low end shows the fft resolution issue. One could restart the analysis with a larger fft to show more precision +x.set(\low,20, \high, 160) -//fourth, equally spaced sines -( -x = { - arg freq = 220; - var source; - source = Mix.fill(7, {arg ind; SinOsc.ar(freq + (ind * (220 / 6)), 0, 0.02)}); - Out.kr(b,FluidSpectralShape.kr(source)); - source.dup; -}.play; -) +// back to full range +x.set(\low,20, \high, 20000) -// this example shows a similar result to the brickwall spectral bandpass above. If we move the central frequency nearer the half-Nyquist: +// free everything +x.free;b.free;c.free;r.stop; +:: -x.set(\freq, 8800) +STRONG::A musical example:: -// we can observe that the linear spread is kept the same, since there is the same linear distance in Hz between our frequencies. Skewness is a good indication here of where we are in the spectrum with the shape. +CODE:: +// todo: port the Max one :: From eae72239074315434d91af6d05fa27729c7cacd2 Mon Sep 17 00:00:00 2001 From: Alex Harker Date: Tue, 21 May 2019 10:23:46 +0100 Subject: [PATCH 42/50] removed ranks from buffer adaptors --- include/SCBufferAdaptor.hpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index 2be53ec..23c4bf1 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -108,13 +108,13 @@ public: return true; } - FluidTensorView samps(size_t channel, size_t rankIdx = 0) override + FluidTensorView samps(size_t channel) override { FluidTensorView v{mBuffer->data, 0, static_cast(mBuffer->frames), static_cast(mBuffer->channels)}; - return v.col(rankIdx + channel * mRank); + return v.col(channel); } // Return a 2D chunk @@ -135,19 +135,16 @@ public: size_t numChans() const override { - return valid() ? static_cast(this->mBuffer->channels) / mRank : 0u; + return valid() ? static_cast(this->mBuffer->channels) : 0u; } - size_t rank() const override { return valid() ? mRank : 0u; } - double sampleRate() const override { return valid() ? mBuffer->samplerate : 0; } - void resize(size_t frames, size_t channels, size_t rank, double sampleRate) override + void resize(size_t frames, size_t channels, double sampleRate) override { SndBuf *thisThing = mBuffer; mOldData = thisThing->data; - mRank = rank; - mWorld->ft->fBufAlloc(mBuffer, static_cast(channels * rank), static_cast(frames), sampleRate); + mWorld->ft->fBufAlloc(mBuffer, static_cast(channels), static_cast(frames), sampleRate); } intptr_t bufnum() { return mBufnum; } @@ -159,7 +156,6 @@ protected: float *mOldData{0}; intptr_t mBufnum; World *mWorld; - size_t mRank{1}; }; std::ostream& operator <<(std::ostream& os, SCBufferAdaptor& b) From 9d0f437975ba043f78b14f92e965835c56b56923 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 30 May 2019 17:20:22 +0100 Subject: [PATCH 43/50] (buf)RTNoveltySlice: wrappers, classes, helps, code --- .../Classes/FluidBufNoveltySlice.sc | 4 +- .../Classes/FluidBufRTNoveltySlice.sc | 21 +++ .../Classes/FluidRTNoveltySlice.sc | 17 ++ .../Classes/FluidBufNoveltySlice.schelp | 2 +- .../Classes/FluidBufRTNoveltySlice.schelp | 169 ++++++++++++++++++ .../Classes/FluidRTNoveltySlice.schelp | 81 +++++++++ src/FluidBufRTNoveltySlice/CMakeLists.txt | 20 +++ .../FluidBufRTNoveltySlice.cpp | 14 ++ src/FluidRTNoveltySlice/CMakeLists.txt | 20 +++ .../FluidRTNoveltySlice.cpp | 13 ++ 10 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 release-packaging/Classes/FluidBufRTNoveltySlice.sc create mode 100644 release-packaging/Classes/FluidRTNoveltySlice.sc create mode 100644 release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp create mode 100644 src/FluidBufRTNoveltySlice/CMakeLists.txt create mode 100644 src/FluidBufRTNoveltySlice/FluidBufRTNoveltySlice.cpp create mode 100644 src/FluidRTNoveltySlice/CMakeLists.txt create mode 100644 src/FluidRTNoveltySlice/FluidRTNoveltySlice.cpp diff --git a/release-packaging/Classes/FluidBufNoveltySlice.sc b/release-packaging/Classes/FluidBufNoveltySlice.sc index b7b934e..c3832e2 100644 --- a/release-packaging/Classes/FluidBufNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufNoveltySlice.sc @@ -1,5 +1,5 @@ FluidBufNoveltySlice{ - *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; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, kernelSize = 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 @@ -12,7 +12,7 @@ FluidBufNoveltySlice{ server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, kernSize, threshold, filterSize, winSize, hopSize, fftSize); + server.sendMsg(\cmd, \BufNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, kernelSize, threshold, filterSize, winSize, hopSize, fftSize); server.sync; indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; action.value(indices); diff --git a/release-packaging/Classes/FluidBufRTNoveltySlice.sc b/release-packaging/Classes/FluidBufRTNoveltySlice.sc new file mode 100644 index 0000000..104f034 --- /dev/null +++ b/release-packaging/Classes/FluidBufRTNoveltySlice.sc @@ -0,0 +1,21 @@ +FluidBufRTNoveltySlice{ + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, winSize = 1024, hopSize = -1, fftSize = -1, action; + + var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + + server = server ? Server.default; + + forkIfNeeded{ + server.sendMsg(\cmd, \BufRTNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, winSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize); + server.sync; + indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; + action.value(indices); + }; + } +} diff --git a/release-packaging/Classes/FluidRTNoveltySlice.sc b/release-packaging/Classes/FluidRTNoveltySlice.sc new file mode 100644 index 0000000..b908004 --- /dev/null +++ b/release-packaging/Classes/FluidRTNoveltySlice.sc @@ -0,0 +1,17 @@ +FluidRTNoveltySlice : UGen { + *ar { arg in = 0, feature = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384, maxKernelSize = 101, maxFilterSize = 100; + ^this.multiNew('audio', in.asAudioRateInput(this), feature, kernelSize, threshold, filterSize, winSize, hopSize, fftSize, maxFFTSize, maxKernelSize, maxFilterSize) + } + checkInputs { + if(inputs.at(8).rate != 'scalar') { + ^(": maxFFTSize cannot be modulated."); + }; + if(inputs.at(9).rate != 'scalar') { + ^(": maxKernelSize cannot be modulated."); + }; + if(inputs.at(10).rate != 'scalar') { + ^(": maxFilterSize cannot be modulated."); + }; + ^this.checkValidInputs; + } +} diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp index a0d7e6c..3c2220e 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp @@ -36,7 +36,7 @@ ARGUMENT:: numChans 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 +ARGUMENT:: kernelSize The granularity of the window in which the algorithm looks for change, in samples. A small number will be sensitive to short term changes, and a large number should look for long term changes. ARGUMENT:: threshold diff --git a/release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp new file mode 100644 index 0000000..586e586 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp @@ -0,0 +1,169 @@ +TITLE:: FluidBufRTNoveltySlice +SUMMARY:: Buffer-Based Novelty-Based Slicer +CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition + + +DESCRIPTION:: +This class implements a non-real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European 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 srcBuf, which channel should be processed. + +ARGUMENT:: numChans + For multichannel srcBuf, how many channel should be summed. + +ARGUMENT:: indices + The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. + +ARGUMENT:: feature + The feature on which novelty is computed. + table:: + ##0 || Spectrum || todo + ##1 || MFCC || todo + ##2 || Pitch || todo + ##3 || Loudness || todo +:: +ARGUMENT:: kernelSize + The granularity of the window in which the algorithm looks for change, in samples. A small number will be sensitive to short term changes, and a large number should look for long term changes. + +ARGUMENT:: threshold + The normalised threshold, between 0 an 1, on the novelty curve to consider it a segmentation point. + +ARGUMENT:: filterSize + The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes. + +ARGUMENT:: winSize + The window size. As novelty estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty + +ARGUMENT:: hopSize + The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + +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 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:: +// load some buffers +( +b = Buffer.read(s,File.realpath(FluidBufRTNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +c = Buffer.new(s); +) + +( +// with basic params +Routine{ + t = Main.elapsedTime; + FluidBufRTNoveltySlice.process(s,b, indices: c, threshold:0.6); + (Main.elapsedTime - t).postln; +}.play +) + +//check the number of slices: it is the number of frames in the transBuf minus the boundary index. +c.query; + +//loops over a splice with the MouseX +( +{ + BufRd.ar(1, b, + Phasor.ar(0,1, + BufRd.kr(1, c, + MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1), + BufRd.kr(1, c, + MouseX.kr(1, BufFrames.kr(c)), 0, 1), + BufRd.kr(1,c, + MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1); + }.play; +) + :: + +STRONG::Examples of the impact of the filterSize:: + + CODE:: +// load some buffers +( +b = Buffer.read(s,File.realpath(FluidBufRTNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); +c = Buffer.new(s); +) + +// process with a given filterSize +FluidBufRTNoveltySlice.process(s,b, indices: c, 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; + +//play slice number 2 +( +{ + BufRd.ar(1, b, + Line.ar( + BufRd.kr(1, c, DC.kr(2), 0, 1), + BufRd.kr(1, c, DC.kr(3), 0, 1), + (BufRd.kr(1, c, DC.kr(3)) - BufRd.kr(1, c, DC.kr(2), 0, 1) + 1) / s.sampleRate), + 0,1); +}.play; +) + +// change the filterSize in the code above to 4. Then to 8. Listen in between to the differences. + +// What's happening? In the first instance (filterSize = 1), the novelty line is jittery and therefore overtriggers on the arpegiated guitar. We also can hear attacks at the end of the segment. Setting the threshold higher (like in the 'Basic Example' pane) misses some more subtle variations. + +// So in the second settings (filterSize = 4), we smooth the novelty line a little, which allows us to catch small differences that are not jittery. It also corrects the ending cutting by the same trick: the averaging of the sharp pick is sliding up, crossing the threshold slightly earlier. + +// If we smooth too much, like the third settings (filterSize = 8), we start to loose precision. Have fun with different values of theshold then will allow you to find the perfect segment for your signal. +:: + +STRONG::A stereo buffer example.:: +CODE:: + +// make a stereo buffer +b = Buffer.alloc(s,88200,2); + +// add some stereo clicks and listen to them +((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)}); +b.play + +// create a new buffer as destinations +c = Buffer.new(s); + +//run the process on them +( +// with basic params +Routine{ + t = Main.elapsedTime; + FluidBufRTNoveltySlice.process(s,b, indices: c, threshold:0.6); + (Main.elapsedTime - t).postln; +}.play +) + +// list the indicies of detected attacks - the two input channels have been summed +c.getn(0,c.numFrames,{|item|item.postln;}) +:: diff --git a/release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp new file mode 100644 index 0000000..58304bc --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp @@ -0,0 +1,81 @@ +TITLE:: FluidRTNoveltySlice +SUMMARY:: Spectral Difference-Based Real-Time Audio 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. 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. + + +CLASSMETHODS:: + +METHOD:: ar + The audio rate version of the object. + +ARGUMENT:: in + The audio to be processed. + +ARGUMENT:: feature + The feature on which novelty is computed. + table:: + ##0 || Spectrum || todo + ##1 || MFCC || todo + ##2 || Pitch || todo + ##3 || Loudness || todo +:: +ARGUMENT:: kernelSize + The granularity of the window in which the algorithm looks for change, in samples. A small number will be sensitive to short term changes, and a large number should look for long term changes. + +ARGUMENT:: threshold + The normalised threshold, between 0 an 1, on the novelty curve to consider it a segmentation point. + +ARGUMENT:: filterSize + The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes. + +ARGUMENT:: 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 + +ARGUMENT:: hopSize + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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:: maxFFTSize + How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. + +ARGUMENT:: maxKernelSize + This cannot be modulated. + +ARGUMENT:: maxFilterSize + This cannot be modulated. + +RETURNS:: + An audio stream with impulses at detected transients. The latency between the input and the output is winSize at maximum. + +EXAMPLES:: + +code:: +//load some sounds +b = Buffer.read(s,File.realpath(FluidRTNoveltySlice.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,loop:1); [FluidRTNoveltySlice.ar(sig,0,3,0.2) * 0.5, DelayN.ar(sig, 1, 1024/ s.sampleRate)]}.play + +// other parameters +{var sig = PlayBuf.ar(1,b,loop:1); [FluidRTNoveltySlice.ar(sig, 0, 31, 0.05, 4, 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,loop:1); + trig = FluidRTNoveltySlice.ar(sig, 0, 0.2, 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/src/FluidBufRTNoveltySlice/CMakeLists.txt b/src/FluidBufRTNoveltySlice/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidBufRTNoveltySlice/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufRTNoveltySlice/FluidBufRTNoveltySlice.cpp b/src/FluidBufRTNoveltySlice/FluidBufRTNoveltySlice.cpp new file mode 100644 index 0000000..c637663 --- /dev/null +++ b/src/FluidBufRTNoveltySlice/FluidBufRTNoveltySlice.cpp @@ -0,0 +1,14 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include +#include + +static InterfaceTable *ft; + +PluginLoad(OfflineFluidDecompositionUGens) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("BufRTNoveltySlice", ft); +} diff --git a/src/FluidRTNoveltySlice/CMakeLists.txt b/src/FluidRTNoveltySlice/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidRTNoveltySlice/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidRTNoveltySlice/FluidRTNoveltySlice.cpp b/src/FluidRTNoveltySlice/FluidRTNoveltySlice.cpp new file mode 100644 index 0000000..8e23423 --- /dev/null +++ b/src/FluidRTNoveltySlice/FluidRTNoveltySlice.cpp @@ -0,0 +1,13 @@ + +// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidRTNoveltySlice", ft); +} From 5c2100fa0be89dbc2b48acae5700b4aab586d346 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 30 May 2019 17:20:52 +0100 Subject: [PATCH 44/50] more examples in AmpSeg and typos in MFCCs --- .../HelpSource/Classes/FluidAmpSlice.schelp | 104 +++++++++++++++++- .../HelpSource/Classes/FluidBufMFCC.schelp | 2 +- .../HelpSource/Classes/FluidMFCC.schelp | 2 +- 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp index 2b5f92f..8a45899 100644 --- a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp @@ -78,7 +78,7 @@ code:: //basic tests: highPass sanity ( {var env, source = SinOsc.ar(320,0,0.5); - env = FluidAmpSlice.ar(source,highPassFreq:2000, outputType:1); + env = FluidAmpSlice.ar(source,highPassFreq:250, outputType:1); [source, env] }.plot(0.03); ) @@ -89,11 +89,105 @@ code:: [source.abs, env] }.plot(0.03); ) +///////////////////////////// //basic tests: absThresh sanity ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:1000, outputType:0, relThreshOn:-144, relThreshOff: -200); - [source, env] - }.plot(0.05); + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12); + [source, env] + }.plot(0.1); +) +//basic tests: absThresh histeresis +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -16); + [source, env] + }.plot(0.1); +) +//basic tests: absThresh min slice +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSliceLength:441); + [source, env] + }.plot(0.1); +) +//basic tests: absThresh min silence +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSilenceLength:441); + [source, env] + }.plot(0.1); +) +//mid tests: absThresh time histeresis on +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minLengthAbove:441, outputType:0); + [DelayN.ar(source,0.1,441/44100), env] + }.plot(0.1); +) +//mid tests: absThresh time histeresis off +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minLengthBelow:441); + [DelayN.ar(source,0.1,441/44100), env] + }.plot(0.1); +) +//mid tests: absThresh with lookBack +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookBack:441); + [DelayN.ar(source,0.1,441/44100), env] + }.plot(0.1); +) +//mid tests: absThresh with lookAhead +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookAhead:441); + [DelayN.ar(source,0.1,441/44100), env] + }.plot(0.1); +) +//mid tests: absThresh with asymetrical lookBack and lookAhead +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookBack:221, lookAhead:441); + [DelayN.ar(source,0.1,441/44100), env] + }.plot(0.1); +) +//advanced tests: absThresh histeresis, long tail +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16); + [source, env] + }.plot(0.1); +) +//solution: have to recut with relThresh +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16, relRampUp:5, relRampDown:200, relThreshOn:-1, relThreshOff:-12); + [source, env] + }.plot(0.1); +) +//beware of double trig +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16, relRampUp:5, relRampDown:200, relThreshOn:-1, relThreshOff:-1); + [source, env] + }.plot(0.025); +) +//a solution: minSliceLength +( + {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16, relRampUp:5, relRampDown:200, relThreshOn:-1, relThreshOff:-1, minSliceLength:441); + [source, env] + }.plot(0.025); +) +//drum slicing, many ways +//load a buffer +b = Buffer.read(s,File.realpath(FluidAmpSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +( + {var env, source = PlayBuf.ar(1,b); + env = FluidAmpSlice.ar(source,absRampUp:2205, absRampDown:2205, absThreshOn:-70, absThreshOff: -80, relRampUp:5, relRampDown:441, relThreshOn:5, relThreshOff:4, minSliceLength:441, outputType:0); + [source, env] + }.plot(1,maxval:[1,10],separately:true); ) :: diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp index 5c47825..a965235 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufMelBands DESCRIPTION:: -This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first decomposed in STRONG::numBands:: perceptually space bands, as in LINK::Classes/FluidMelBands::. It is then analysed in STRONG::numCoefs:: number of cepstral coefficients. It has the avantage to be amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: +This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoefs:: number of cepstral coefficients. It has the avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: The process will return a single multichannel buffer of STRONG::numCoefs:: per input channel. Each frame represents a value, which is every hopSize. diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp index 05905e9..61e4ed5 100644 --- a/release-packaging/HelpSource/Classes/FluidMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidMFCC.schelp @@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMelBands DESCRIPTION:: -This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first decomposed in STRONG::numBands:: perceptually space bands, as in LINK::Classes/FluidMelBands::. It is then analysed in STRONG::numCoefs:: number of cepstral coefficients. It has the avantage to be amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: +This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoefs:: number of cepstral coefficients. It has the avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: The process will return a multichannel control steam of STRONG::maxNumCoefs::, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. From fd72e78e4c5d2df609c6a4b58662b25a9cfd21ca Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 30 May 2019 18:20:53 +0100 Subject: [PATCH 45/50] confirmed that (buf)spectralshape is now in Hz --- .../Classes/FluidBufSpectralShape.schelp | 12 ++++++------ .../Classes/FluidSpectralShape.schelp | 18 ++++++------------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index 78250cc..fc65a97 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -11,12 +11,12 @@ The descriptors are: LIST:: ##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: LIST:: - ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. + ## the spectral centroid (1) in Hz. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. + ## the spectral spread (2) in Hz. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. + ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below that frequency, i.e. the central bin of the magnitude spectrum, it is positive. ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. :: - ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. + ## the rolloff (5) in Hz. This indicates the frequency under which 95% of the energy is included. ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. @@ -86,7 +86,7 @@ Routine{ // listen to the source and look at the buffer b.play; -c.plot(minval:-5, maxval:250) +c.plot(separately:true) :: STRONG::A stereo buffer example.:: @@ -115,5 +115,5 @@ Routine{ ) // look at the buffer: 7shapes for left, then 7 shapes for right -c.plot(minval:-25, maxval:150) +c.plot(separately:true) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp index 5dc28c4..8436237 100644 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp @@ -10,12 +10,12 @@ The descriptors are: LIST:: ##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as: LIST:: - ## the spectral centroid (1) in spectral bins as units. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. - ## the spectral spread (2) in spectral bins. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. - ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below the bin representing that frequency, i.e. the central bin of the magnitude spectrum, it is positive. + ## the spectral centroid (1) in Hz. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum. + ## the spectral spread (2) in Hz. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid. + ## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below that frequency, i.e. the central bin of the magnitude spectrum, it is positive. ## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high. :: - ## the rolloff (5) in bin number. This indicates the bin under which 95% of the energy is included. + ## the rolloff (5) in Hz. This indicates the frequency under which 95% of the energy is included. ## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is. ## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve. @@ -129,7 +129,7 @@ a = Array.fill(7, {arg i; }); ) -// this time, update a little more slowly, and convert in Hz the 3 descriptors published in bins by the algorythm. +// this time, update a little more slowly. ( r = Routine { { @@ -138,13 +138,7 @@ r = Routine { { if(w.isClosed.not) { val.do({arg item,index; - if ((index < 2) || (index == 4)) - { - a[index].string = (item * s.sampleRate / 1024).round(0.01); - } { - a[index].string = item.round(0.01); - }; - }) + a[index].string = item.round(0.01)}) } }.defer }); From be6a856844152a957796c28602728da91b567d6a Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 30 May 2019 18:30:20 +0100 Subject: [PATCH 46/50] spectralshapes - typos --- .../HelpSource/Classes/FluidBufSpectralShape.schelp | 4 ++-- .../HelpSource/Classes/FluidSpectralShape.schelp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index fc65a97..e269173 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -52,10 +52,10 @@ ARGUMENT:: features The destination buffer for the 7 spectral features describing the spectral shape. 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 + The window size. As spectral shape estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. + The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. 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. diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp index 8436237..6985267 100644 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp @@ -33,10 +33,10 @@ ARGUMENT:: in The audio to be processed. 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 + The window size. As spectral shape estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of 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. From abeb868a6541f8fda9442fa67bd122e05106e6c1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 3 Jun 2019 14:56:10 +0100 Subject: [PATCH 47/50] ssshhh --- include/FluidSCWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index b7219d0..f93aa09 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -139,7 +139,7 @@ public: { if (mOutputConnections[i]) mOutputs[i].reset(out(static_cast(i)), 0, fullBufferSize()); } - for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(i), 0, 1); } + for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(static_cast(i)), 0, 1); } mClient.process(mAudioInputs, mOutputs); } From 4d372c3dc0f2082bebb82543500514b26a1114b9 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 4 Jun 2019 16:20:49 -0300 Subject: [PATCH 48/50] new interface naming convention --- release-packaging/Classes/FluidBufHPSS.sc | 6 +- release-packaging/Classes/FluidBufLoudness.sc | 6 +- release-packaging/Classes/FluidBufMFCC.sc | 6 +- release-packaging/Classes/FluidBufMelBands.sc | 6 +- release-packaging/Classes/FluidBufNMF.sc | 4 +- .../Classes/FluidBufNoveltySlice.sc | 6 +- .../Classes/FluidBufOnsetSlice.sc | 6 +- release-packaging/Classes/FluidBufPitch.sc | 6 +- release-packaging/Classes/FluidBufSines.sc | 6 +- .../Classes/FluidBufSpectralShape.sc | 6 +- .../Classes/FluidBufTransientSlice.sc | 4 +- .../Classes/FluidBufTransients.sc | 4 +- release-packaging/Classes/FluidHPSS.sc | 4 +- release-packaging/Classes/FluidLoudness.sc | 6 +- release-packaging/Classes/FluidMFCC.sc | 4 +- release-packaging/Classes/FluidMelBands.sc | 4 +- release-packaging/Classes/FluidNMFFilter.sc | 6 +- release-packaging/Classes/FluidNMFMatch.sc | 6 +- release-packaging/Classes/FluidOnsetSlice.sc | 4 +- release-packaging/Classes/FluidPitch.sc | 4 +- release-packaging/Classes/FluidSTFTPass.sc | 4 +- release-packaging/Classes/FluidSines.sc | 4 +- .../Classes/FluidSpectralShape.sc | 4 +- .../Classes/FluidTransientSlice.sc | 4 +- release-packaging/Classes/FluidTransients.sc | 4 +- .../HelpSource/Classes/FluidBufHPSS.schelp | 4 +- .../Classes/FluidBufLoudness.schelp | 4 +- .../HelpSource/Classes/FluidBufMFCC.schelp | 4 +- .../Classes/FluidBufMelBands.schelp | 4 +- .../HelpSource/Classes/FluidBufNMF.schelp | 60 +++++++++---------- .../Classes/FluidBufNoveltySlice.schelp | 4 +- .../Classes/FluidBufOnsetSlice.schelp | 6 +- .../HelpSource/Classes/FluidBufPitch.schelp | 10 ++-- .../HelpSource/Classes/FluidBufSines.schelp | 2 +- .../Classes/FluidBufSpectralShape.schelp | 2 +- .../Classes/FluidBufTransientSlice.schelp | 6 +- .../Classes/FluidBufTransients.schelp | 6 +- .../HelpSource/Classes/FluidHPSS.schelp | 8 +-- .../HelpSource/Classes/FluidLoudness.schelp | 10 ++-- .../HelpSource/Classes/FluidMFCC.schelp | 6 +- .../HelpSource/Classes/FluidMelBands.schelp | 6 +- .../HelpSource/Classes/FluidNMFFilter.schelp | 34 +++++------ .../HelpSource/Classes/FluidNMFMatch.schelp | 33 +++++----- .../HelpSource/Classes/FluidOnsetSlice.schelp | 12 ++-- .../HelpSource/Classes/FluidPitch.schelp | 8 +-- .../HelpSource/Classes/FluidSTFTPass.schelp | 6 +- .../HelpSource/Classes/FluidSines.schelp | 12 ++-- .../Classes/FluidSpectralShape.schelp | 6 +- .../Classes/FluidTransientSlice.schelp | 10 ++-- .../HelpSource/Classes/FluidTransients.schelp | 4 +- .../fileiterator-2passes.sc | 7 ++- .../ignore/Examples/nmf/JiT-NMF.scd | 4 +- 52 files changed, 201 insertions(+), 201 deletions(-) diff --git a/release-packaging/Classes/FluidBufHPSS.sc b/release-packaging/Classes/FluidBufHPSS.sc index 6d4882f..77b5b8b 100644 --- a/release-packaging/Classes/FluidBufHPSS.sc +++ b/release-packaging/Classes/FluidBufHPSS.sc @@ -1,7 +1,7 @@ FluidBufHPSS{ - *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; + *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, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; harmonic = harmonic.asUGenInput; @@ -19,7 +19,7 @@ FluidBufHPSS{ //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, 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.sendMsg(\cmd, \BufHPSS, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize); server.sync; 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}; diff --git a/release-packaging/Classes/FluidBufLoudness.sc b/release-packaging/Classes/FluidBufLoudness.sc index 402139e..58f3f8e 100644 --- a/release-packaging/Classes/FluidBufLoudness.sc +++ b/release-packaging/Classes/FluidBufLoudness.sc @@ -1,7 +1,7 @@ FluidBufLoudness{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, winSize = 1024, hopSize = 512, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action; - var maxWinSize = winSize.nextPowerOfTwo; + var maxwindowSize = windowSize.nextPowerOfTwo; source = source.asUGenInput; features = features.asUGenInput; @@ -12,7 +12,7 @@ FluidBufLoudness{ server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufLoudness, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, winSize, hopSize, maxWinSize); + server.sendMsg(\cmd, \BufLoudness, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize); server.sync; features = server.cachedBufferAt(features); features.updateInfo; server.sync; action.value(features); diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index 6a6c820..5614dc2 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -1,7 +1,7 @@ FluidBufMFCC{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; features = features.asUGenInput; @@ -16,7 +16,7 @@ FluidBufMFCC{ // same goes to maxNumCoefs, which is passed numCoefs in this case forkIfNeeded{ - server.sendMsg(\cmd, \BufMFCC, source, startFrame, numFrames, startChan, numChans, features, numCoefs, numBands, minFreq, maxFreq, numCoefs, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufMFCC, source, startFrame, numFrames, startChan, numChans, features, numCoefs, numBands, minFreq, maxFreq, numCoefs, windowSize, hopSize, fftSize, maxFFTSize); server.sync; features = server.cachedBufferAt(features); features.updateInfo; server.sync; action.value(features); diff --git a/release-packaging/Classes/FluidBufMelBands.sc b/release-packaging/Classes/FluidBufMelBands.sc index 2b7c770..5583ab1 100644 --- a/release-packaging/Classes/FluidBufMelBands.sc +++ b/release-packaging/Classes/FluidBufMelBands.sc @@ -1,7 +1,7 @@ FluidBufMelBands{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; features = features.asUGenInput; @@ -16,7 +16,7 @@ FluidBufMelBands{ // same for maxNumBands which is passed numBands forkIfNeeded{ - server.sendMsg(\cmd, \BufMelBands, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufMelBands, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, windowSize, hopSize, fftSize, maxFFTSize); server.sync; features = server.cachedBufferAt(features); features.updateInfo; server.sync; action.value(features); diff --git a/release-packaging/Classes/FluidBufNMF.sc b/release-packaging/Classes/FluidBufNMF.sc index 8b166bd..a46879b 100644 --- a/release-packaging/Classes/FluidBufNMF.sc +++ b/release-packaging/Classes/FluidBufNMF.sc @@ -1,5 +1,5 @@ FluidBufNMF { - *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; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action; source = source.asUGenInput; @@ -16,7 +16,7 @@ FluidBufNMF { activations = activations ? -1; forkIfNeeded{ - server.sendMsg(\cmd, \BufNMF, source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, rank, numIter, winSize, hopSize, fftSize); + server.sendMsg(\cmd, \BufNMF, source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize); server.sync; if (destination != -1) {destination = server.cachedBufferAt(destination); destination.updateInfo; server.sync;} {destination = nil}; if (bases != -1) {bases = server.cachedBufferAt(bases); bases.updateInfo; server.sync;} {bases = nil}; diff --git a/release-packaging/Classes/FluidBufNoveltySlice.sc b/release-packaging/Classes/FluidBufNoveltySlice.sc index c3832e2..e55af95 100644 --- a/release-packaging/Classes/FluidBufNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufNoveltySlice.sc @@ -1,7 +1,7 @@ FluidBufNoveltySlice{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, kernelSize = 3, threshold = 0.8, filterSize = 1, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, kernelSize = 3, threshold = 0.8, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action; - //var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; //ready for when we need it from the RT wrapper + //var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; //ready for when we need it from the RT wrapper source = source.asUGenInput; indices = indices.asUGenInput; @@ -12,7 +12,7 @@ FluidBufNoveltySlice{ server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, kernelSize, threshold, filterSize, winSize, hopSize, fftSize); + server.sendMsg(\cmd, \BufNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize); server.sync; 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 2c4dcb0..d7bbcac 100644 --- a/release-packaging/Classes/FluidBufOnsetSlice.sc +++ b/release-packaging/Classes/FluidBufOnsetSlice.sc @@ -1,7 +1,7 @@ FluidBufOnsetSlice{ - *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; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, function = 0, threshold = 0.1, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; indices = indices.asUGenInput; @@ -15,7 +15,7 @@ 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, source, startFrame, numFrames, startChan, numChans, indices, function, threshold, debounce, filterSize, frameDelta, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufOnsetSlice, source, startFrame, numFrames, startChan, numChans, indices, function, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize); server.sync; indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; action.value(indices); diff --git a/release-packaging/Classes/FluidBufPitch.sc b/release-packaging/Classes/FluidBufPitch.sc index 566be36..d0ad4ce 100644 --- a/release-packaging/Classes/FluidBufPitch.sc +++ b/release-packaging/Classes/FluidBufPitch.sc @@ -1,7 +1,7 @@ FluidBufPitch{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; features = features.asUGenInput; @@ -15,7 +15,7 @@ FluidBufPitch{ //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, \BufPitch, source, startFrame, numFrames, startChan, numChans, features, algorithm, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufPitch, source, startFrame, numFrames, startChan, numChans, features, algorithm, windowSize, hopSize, fftSize, maxFFTSize); server.sync; features = server.cachedBufferAt(features); features.updateInfo; server.sync; action.value(features); diff --git a/release-packaging/Classes/FluidBufSines.sc b/release-packaging/Classes/FluidBufSines.sc index d54520a..0da582b 100644 --- a/release-packaging/Classes/FluidBufSines.sc +++ b/release-packaging/Classes/FluidBufSines.sc @@ -1,7 +1,7 @@ FluidBufSines{ - *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; + *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, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; sines = sines.asUGenInput; @@ -17,7 +17,7 @@ FluidBufSines{ //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, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufSines, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, maxFFTSize); server.sync; if (sines != -1) {sines = server.cachedBufferAt(sines); sines.updateInfo; server.sync;} {sines = nil}; if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil}; diff --git a/release-packaging/Classes/FluidBufSpectralShape.sc b/release-packaging/Classes/FluidBufSpectralShape.sc index 2105a8e..72b8b61 100644 --- a/release-packaging/Classes/FluidBufSpectralShape.sc +++ b/release-packaging/Classes/FluidBufSpectralShape.sc @@ -1,7 +1,7 @@ FluidBufSpectralShape{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; features = features.asUGenInput; @@ -15,7 +15,7 @@ FluidBufSpectralShape{ //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, \BufSpectralShape, source, startFrame, numFrames, startChan, numChans, features, winSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufSpectralShape, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize); server.sync; features = server.cachedBufferAt(features); features.updateInfo; server.sync; action.value(features); diff --git a/release-packaging/Classes/FluidBufTransientSlice.sc b/release-packaging/Classes/FluidBufTransientSlice.sc index f4b6d7f..25fb83a 100644 --- a/release-packaging/Classes/FluidBufTransientSlice.sc +++ b/release-packaging/Classes/FluidBufTransientSlice.sc @@ -1,5 +1,5 @@ FluidBufTransientSlice{ - *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; + *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, windowSize = 14, clumpLength = 25, minSliceLength = 1000, action; source = source.asUGenInput; indices = indices.asUGenInput; @@ -10,7 +10,7 @@ FluidBufTransientSlice{ server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufTransientSlice, source, startFrame, numFrames, startChan, numChans, indices, 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, windowSize, clumpLength, minSliceLength); server.sync; 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 7a2cad9..521ef50 100644 --- a/release-packaging/Classes/FluidBufTransients.sc +++ b/release-packaging/Classes/FluidBufTransients.sc @@ -1,5 +1,5 @@ FluidBufTransients { - *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; + *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, windowSize = 14, clumpLength = 25, action; source = source.asUGenInput; transients = transients.asUGenInput; @@ -12,7 +12,7 @@ FluidBufTransients { residual = residual ? -1; forkIfNeeded{ - server.sendMsg(\cmd, \BufTransients, source, startFrame, numFrames, startChan, numChans, transients, residual, 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, windowSize, clumpLength); server.sync; if (transients != -1) {transients = server.cachedBufferAt(transients); transients.updateInfo; server.sync;} {transients = nil}; if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil}; diff --git a/release-packaging/Classes/FluidHPSS.sc b/release-packaging/Classes/FluidHPSS.sc index 85c7c8d..44e92bf 100644 --- a/release-packaging/Classes/FluidHPSS.sc +++ b/release-packaging/Classes/FluidHPSS.sc @@ -1,6 +1,6 @@ FluidHPSS : MultiOutUGen { - *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) + *ar { arg in = 0, harmFilterSize=17, percFilterSize = 31, maskingMode=0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384, maxHarmFilterSize = 101, maxPercFilterSize = 101; + ^this.multiNew('audio', in.asAudioRateInput(this), harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, maxHarmFilterSize, maxPercFilterSize) } init { arg ... theInputs; inputs = theInputs; diff --git a/release-packaging/Classes/FluidLoudness.sc b/release-packaging/Classes/FluidLoudness.sc index ef75218..36bece6 100644 --- a/release-packaging/Classes/FluidLoudness.sc +++ b/release-packaging/Classes/FluidLoudness.sc @@ -1,6 +1,6 @@ FluidLoudness : MultiOutUGen { - *kr { arg in = 0, kWeighting = 1, truePeak = 1, winSize = 1024, hopSize = 512, maxWinSize = 16384; - ^this.multiNew('control', in.asAudioRateInput(this), kWeighting, truePeak, winSize, hopSize, maxWinSize); + *kr { arg in = 0, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, maxwindowSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), kWeighting, truePeak, windowSize, hopSize, maxwindowSize); } init {arg ...theInputs; @@ -10,7 +10,7 @@ FluidLoudness : MultiOutUGen { checkInputs { if(inputs.at(5).rate != 'scalar') { - ^(": maxWinSize cannot be modulated."); + ^(": maxwindowSize cannot be modulated."); }; ^this.checkValidInputs; } diff --git a/release-packaging/Classes/FluidMFCC.sc b/release-packaging/Classes/FluidMFCC.sc index 222d72f..3c06371 100644 --- a/release-packaging/Classes/FluidMFCC.sc +++ b/release-packaging/Classes/FluidMFCC.sc @@ -1,7 +1,7 @@ FluidMFCC : MultiOutUGen { - *kr { arg in = 0, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumCoefs = 40, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('control', in.asAudioRateInput(this), numCoefs, numBands, minFreq, maxFreq, maxNumCoefs, winSize, hopSize, fftSize, maxFFTSize); + *kr { arg in = 0, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumCoefs = 40, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), numCoefs, numBands, minFreq, maxFreq, maxNumCoefs, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; diff --git a/release-packaging/Classes/FluidMelBands.sc b/release-packaging/Classes/FluidMelBands.sc index 5055791..3021006 100644 --- a/release-packaging/Classes/FluidMelBands.sc +++ b/release-packaging/Classes/FluidMelBands.sc @@ -1,7 +1,7 @@ FluidMelBands : MultiOutUGen { - *kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 120, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('control', in.asAudioRateInput(this), numBands, minFreq, maxFreq, maxNumBands, winSize, hopSize, fftSize, maxFFTSize); + *kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 120, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), numBands, minFreq, maxFreq, maxNumBands, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; diff --git a/release-packaging/Classes/FluidNMFFilter.sc b/release-packaging/Classes/FluidNMFFilter.sc index 757e8b2..a4ab279 100644 --- a/release-packaging/Classes/FluidNMFFilter.sc +++ b/release-packaging/Classes/FluidNMFFilter.sc @@ -1,7 +1,7 @@ FluidNMFFilter : MultiOutUGen { - *ar { arg in = 0, bases, maxRank = 1, numIter = 10, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('audio', in.asAudioRateInput(this), bases, maxRank, numIter, winSize, hopSize, fftSize, maxFFTSize); + *ar { arg in = 0, bases, maxComponents = 1, iterations = 10, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('audio', in.asAudioRateInput(this), bases, maxComponents, iterations, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; @@ -11,7 +11,7 @@ FluidNMFFilter : MultiOutUGen { checkInputs { if(inputs.at(2).rate != 'scalar') { - ^(": maxRank cannot be modulated."); + ^(": maxComponents cannot be modulated."); }; if(inputs.at(7).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); diff --git a/release-packaging/Classes/FluidNMFMatch.sc b/release-packaging/Classes/FluidNMFMatch.sc index 68ef57f..0c438d5 100644 --- a/release-packaging/Classes/FluidNMFMatch.sc +++ b/release-packaging/Classes/FluidNMFMatch.sc @@ -1,7 +1,7 @@ FluidNMFMatch : MultiOutUGen { - *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); + *kr { arg in = 0, bases, maxComponents = 1, iterations = 10, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), bases, maxComponents, iterations, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; @@ -11,7 +11,7 @@ FluidNMFMatch : MultiOutUGen { checkInputs { if(inputs.at(2).rate != 'scalar') { - ^(": maxRank cannot be modulated."); + ^(": maxComponents cannot be modulated."); }; if(inputs.at(7).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); diff --git a/release-packaging/Classes/FluidOnsetSlice.sc b/release-packaging/Classes/FluidOnsetSlice.sc index b50980a..2bedb64 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, 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) + *ar { arg in = 0, function = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('audio', in.asAudioRateInput(this), function, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize) } checkInputs { if(inputs.at(9).rate != 'scalar') { diff --git a/release-packaging/Classes/FluidPitch.sc b/release-packaging/Classes/FluidPitch.sc index d574407..66bdfb9 100644 --- a/release-packaging/Classes/FluidPitch.sc +++ b/release-packaging/Classes/FluidPitch.sc @@ -1,7 +1,7 @@ FluidPitch : MultiOutUGen { - *kr { arg in = 0, algorithm = 2, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('control', in.asAudioRateInput(this), algorithm, winSize, hopSize, fftSize, maxFFTSize); + *kr { arg in = 0, algorithm = 2, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), algorithm, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; diff --git a/release-packaging/Classes/FluidSTFTPass.sc b/release-packaging/Classes/FluidSTFTPass.sc index caa0005..d3456fd 100644 --- a/release-packaging/Classes/FluidSTFTPass.sc +++ b/release-packaging/Classes/FluidSTFTPass.sc @@ -1,6 +1,6 @@ FluidSTFTPass : UGen { - *ar { arg in = 0, winSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384; - ^this.multiNew('audio', in.asAudioRateInput(this), winSize, hopSize, fftSize, maxFFTSize) + *ar { arg in = 0, windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384; + ^this.multiNew('audio', in.asAudioRateInput(this), windowSize, hopSize, fftSize, maxFFTSize) } checkInputs { if(inputs.at(4).rate != 'scalar') { diff --git a/release-packaging/Classes/FluidSines.sc b/release-packaging/Classes/FluidSines.sc index 3d2f16a..5edf880 100644 --- a/release-packaging/Classes/FluidSines.sc +++ b/release-packaging/Classes/FluidSines.sc @@ -1,6 +1,6 @@ FluidSines : MultiOutUGen { - *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) + *ar { arg in = 0, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1.0, windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize=16384; + ^this.multiNew('audio', in.asAudioRateInput(this), bandwidth, threshold, minTrackLen, magWeight, freqWeight ,windowSize, hopSize, fftSize, maxFFTSize) } init { arg ... theInputs; inputs = theInputs; diff --git a/release-packaging/Classes/FluidSpectralShape.sc b/release-packaging/Classes/FluidSpectralShape.sc index 216ea7c..ada4f20 100644 --- a/release-packaging/Classes/FluidSpectralShape.sc +++ b/release-packaging/Classes/FluidSpectralShape.sc @@ -1,7 +1,7 @@ FluidSpectralShape : MultiOutUGen { - *kr { arg in = 0, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('control', in.asAudioRateInput(this), winSize, hopSize, fftSize, maxFFTSize); + *kr { arg in = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; diff --git a/release-packaging/Classes/FluidTransientSlice.sc b/release-packaging/Classes/FluidTransientSlice.sc index 1b9517a..b4f4ecc 100644 --- a/release-packaging/Classes/FluidTransientSlice.sc +++ b/release-packaging/Classes/FluidTransientSlice.sc @@ -1,5 +1,5 @@ FluidTransientSlice : UGen { - *ar { arg in = 0, order = 20, blockSize = 256, padSize = 128, skew = 0.0, threshFwd = 2.0, threshBack = 1.1, winSize=14, debounce=25, minSlice = 1000; - ^this.multiNew('audio', in.asAudioRateInput(this), order, blockSize, padSize, skew, threshFwd ,threshBack, winSize, debounce, minSlice) + *ar { arg in = 0, order = 20, blockSize = 256, padSize = 128, skew = 0.0, threshFwd = 2.0, threshBack = 1.1, windowSize=14, clumpLength=25, minSliceLength = 1000; + ^this.multiNew('audio', in.asAudioRateInput(this), order, blockSize, padSize, skew, threshFwd ,threshBack, windowSize, clumpLength, minSliceLength) } } diff --git a/release-packaging/Classes/FluidTransients.sc b/release-packaging/Classes/FluidTransients.sc index fa8d735..ef0c968 100644 --- a/release-packaging/Classes/FluidTransients.sc +++ b/release-packaging/Classes/FluidTransients.sc @@ -1,6 +1,6 @@ FluidTransients : MultiOutUGen { - *ar { arg in = 0, order = 20, blockSize = 256, padSize = 128, skew = 0.0, threshFwd = 2.0, threshBack = 1.1, winSize=14, debounce=25; - ^this.multiNew('audio', in.asAudioRateInput(this), order, blockSize, padSize, skew, threshFwd, threshBack, winSize, debounce) + *ar { arg in = 0, order = 20, blockSize = 256, padSize = 128, skew = 0.0, threshFwd = 2.0, threshBack = 1.1, windowSize=14, clumpLength=25; + ^this.multiNew('audio', in.asAudioRateInput(this), order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength) } init { arg ... theInputs; inputs = theInputs; diff --git a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp index c17540c..5532826 100644 --- a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp @@ -94,7 +94,7 @@ ARGUMENT:: percThreshFreq2 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 +ARGUMENT:: windowSize The window size in samples. As HPSS relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize @@ -146,7 +146,7 @@ d.play; ( Routine{ t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,winSize:4096,hopSize:512); + FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512); (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp index 3b41cd7..7cd256e 100644 --- a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp @@ -41,7 +41,7 @@ ARGUMENT:: kWeighting ARGUMENT:: truePeak A flag to switch the computation of TruePeak. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple absolute peak of the frame. -ARGUMENT:: winSize +ARGUMENT:: windowSize The size of the window on which the computation is done. By default 1024 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. ARGUMENT:: hopSize @@ -105,7 +105,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufLoudness.process(s, b, features: c, winSize: 17640, hopSize:4410); + FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410); (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp index a965235..e2aabf4 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -46,7 +46,7 @@ ARGUMENT:: minFreq ARGUMENT:: maxFreq The highest boundary of the highest band of the model, in Hz. -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As MFCC computation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize @@ -109,6 +109,6 @@ Routine{ }.play ) -// look at the buffer: 5 coefs for left, then 5 coefs for right +// look at the buffer: 5 coefs for left, then 5 coefs for right (the first of each is linked to the loudness) c.plot(separately:true) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp index 4d95dc4..5643f92 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -44,7 +44,7 @@ ARGUMENT:: minFreq ARGUMENT:: maxFreq The highest boundary of the highest band of the model, in Hz. -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As spectral description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize @@ -108,5 +108,5 @@ Routine{ ) // look at the buffer: 10 bands for left, then 10 bands for right -c.plot(minval:0, maxval:100) +c.plot(separately:true) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index ae37561..092d5c6 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -8,7 +8,7 @@ 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 ('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 algorithm takes a buffer in and divides it into a number of components, determined by the STRONG::Components:: argument. It works iteratively, by trying to find a combination of spectral templates ('bases') and envelopes ('activations') that yield the original magnitude spectrogram when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results. The object can return either or all of the following: LIST:: ## a spectral contour of each component in the form of a magnitude spectrogram (called a basis in NMF lingo); @@ -20,8 +20,8 @@ The bases and activations can be used to make a kind of vocoder based on what NM 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:: -## 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 +## bases must be STRONG::(fft size / 2) + 1:: frames and STRONG::(components * input channels):: channels +## activations must be STRONG::(input frames / hopSize) + 1:: frames and STRONG::(components * input channels):: channels :: In this implementation, the components are reconstructed by masking the original spectrum, such that they will sum to yield the original sound. @@ -57,7 +57,7 @@ ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. 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. + The index of the buffer where the different reconstructed components will be reconstructed. The buffer will be resized to STRONG::components * numChannelsProcessed:: channels and STRONG::sourceDuration:: lenght. If STRONG::nil:: is provided, the reconstruction will not happen. ARGUMENT:: bases The index of the buffer where the different bases will be written to and/or read from: the behaviour is set in the following argument. If STRONG::nil:: is provided, no bases will be returned. @@ -65,7 +65,7 @@ ARGUMENT:: bases ARGUMENT:: basesMode This flag decides of how the basis buffer passed as the previous argument is treated. table:: - ## 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. + ## 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::components * 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. :: @@ -76,18 +76,18 @@ ARGUMENT:: activations 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. + ## 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::components * numChannelsProcessed:: channels and STRONG::(sourceDuration / hopsize + 1):: lenght. ## 1 || The passed buffer is considered as seed for the activations. Its dimensions should match the values above. The resulting activations will replace the seed ones. ## 2 || The passed buffer is considered as a template for the activations, and will therefore not change. Its dimensions should match the values above. :: -ARGUMENT:: rank +ARGUMENT:: components The number of elements the NMF algorithm will try to divide the spectrogram of the source in. -ARGUMENT:: numIter +ARGUMENT:: iterations The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU expensive, lower numbers will be more unpredictable in quality. -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As NMF relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize @@ -96,10 +96,10 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. -ARGUMENT:: winType +ARGUMENT:: windowType The inner FFT/IFFT windowing type (not implemented yet) -ARGUMENT:: randSeed +ARGUMENT:: randomSeed 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 @@ -140,16 +140,16 @@ d.plot d.play //////(beware !!!! loud!!!) ( -// separate them in 2 ranks +// separate them in 2 components Routine { - FluidBufNMF.process(s, d, destination:e, bases: f, activations:g, rank:2); + FluidBufNMF.process(s, d, destination:e, bases: f, activations:g, components:2); e.query; f.query; g.query; }.play ) -// look at the resynthesised separated signal +// look at the resynthesised separated components as audio e.plot; // look at the bases signal for 2 spikes @@ -173,7 +173,7 @@ y = Buffer.new(s); ~fft_size = 1024; ~frame_size = 512; ~hop_size = 256; -~which_rank = 3; +~which_component = 3; ) // matrix factorisation, requesting everything - wait for the computation time to appear. @@ -185,16 +185,16 @@ Routine{ }.play ) -//look at the resynthesised ranks, the bases and the activations +//look at the resynthesised components, the bases and the activations c.plot; x.plot; y.plot; //null test of the sum of sources {(PlayBuf.ar(5,c,doneAction:2).sum)+(-1*PlayBuf.ar(1,b,doneAction:2))}.play -// play the ranks spread in the stereo field +// play the components spread in the stereo field {Splay.ar(PlayBuf.ar(5,c,doneAction:2))}.play //play a single source -{PlayBuf.ar(5,c,doneAction:2)[~which_rank].dup}.play +{PlayBuf.ar(5,c,doneAction:2)[~which_component].dup}.play //play noise using one of the bases as filter. ( @@ -203,7 +203,7 @@ c.plot; x.plot; y.plot; chain = FFT(LocalBuf(~fft_size), WhiteNoise.ar()); chain = chain.pvcollect(~fft_size, {|mag, phase, index| - [mag * BufRd.kr(5,x,DC.kr(index),0,1)[~which_rank]]; + [mag * BufRd.kr(5,x,DC.kr(index),0,1)[~which_component]]; }); IFFT(chain); @@ -211,16 +211,16 @@ c.plot; x.plot; y.plot; ) //play noise using one of the activations as envelope. -{WhiteNoise.ar(BufRd.kr(5,y,Phasor.ar(1,1/~hop_size,0,(b.numFrames / ~hop_size + 1)),0,1)[~which_rank])*0.5}.play +{WhiteNoise.ar(BufRd.kr(5,y,Phasor.ar(1,1/~hop_size,0,(b.numFrames / ~hop_size + 1)),0,1)[~which_component])*0.5}.play //play noise through both matching activation and filter ( { var chain; - chain = FFT(LocalBuf(~fft_size), WhiteNoise.ar(BufRd.kr(5,y,Phasor.ar(1,1/~hop_size,0,(b.numFrames / ~hop_size + 1)),0,1)[~which_rank]*12),0.5,1); + chain = FFT(LocalBuf(~fft_size), WhiteNoise.ar(BufRd.kr(5,y,Phasor.ar(1,1/~hop_size,0,(b.numFrames / ~hop_size + 1)),0,1)[~which_component]*12),0.5,1); chain = chain.pvcollect(~fft_size, {|mag, phase, index| - [mag * BufRd.kr(5,x,DC.kr(index),0,1)[~which_rank]]; + [mag * BufRd.kr(5,x,DC.kr(index),0,1)[~which_component]]; }); [0,IFFT(chain)]; @@ -244,18 +244,18 @@ y = Buffer.new(s); // train only 2 seconds ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, x, rank:10); + FluidBufNMF.process(s,b,0,88200,0,1, c, x, components:10); c.query; }.play; ) -// find the rank that has the picking sound by changing which channel to listen to +// find the component that has the picking sound by changing which channel to listen to ( ~element = 4; {PlayBuf.ar(10,c)[~element]}.play ) -// copy all the other ranks on itself and the picking basis as the sole component of the 1st channel +// copy all the other components on itself and the picking basis as the sole component of the 1st channel ( Routine{ z = (0..9); @@ -268,7 +268,7 @@ Routine{ //process the whole file, splitting it with the 2 trained bases ( Routine{ - FluidBufNMF.process(s, b, destination: c, bases: e, basesMode: 2, activations: y, rank:2); + FluidBufNMF.process(s, b, destination: c, bases: e, basesMode: 2, activations: y, components:2); c.query; }.play; ) @@ -311,7 +311,7 @@ d.plot d.play //////(beware !!!! loud!!!) ( -//make a seeding basis of 3 ranks: +//make a seeding basis of 3 components: var highpass, lowpass, direct; highpass = Array.fill(513,{|i| (i < 50).asInteger}); lowpass = 1 - highpass; @@ -326,7 +326,7 @@ e.query ( // use the seeding basis, without updating Routine { - FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 2, activations:g, rank:3); + FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 2, activations:g, components:3); e.query; f.query; g.query; @@ -345,7 +345,7 @@ g.plot; ( // use the seeding bases, with updating this time Routine { - FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 1, activations:g, rank:3); + FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 1, activations:g, components:3); e.query; f.query; g.query; @@ -358,6 +358,6 @@ f.plot; // look at the bases that have now updated in place (with the 3rd channel being more focused e.plot; -// look at the activations (sharper 3rd rank at transitions) +// look at the activations (sharper 3rd component at transitions) g.plot; :: diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp index 3c2220e..92d3842 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp @@ -45,7 +45,7 @@ ARGUMENT:: threshold 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 +ARGUMENT:: windowSize The window size. As novelty estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize @@ -107,7 +107,7 @@ c = Buffer.new(s); ) // process with a given filterSize -FluidBufNoveltySlice.process(s,b, indices: c, kernSize:31, threshold:0.3, filterSize:0) +FluidBufNoveltySlice.process(s,b, indices: c, kernelSize: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 index 83e5041..ecd692f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp @@ -52,7 +52,7 @@ ARGUMENT:: function ARGUMENT:: threshold The thresholding of a new slice. Value ranges are different for each function, from 0 upwards. -ARGUMENT:: debounce +ARGUMENT:: minSliceLength The minimum duration of a slice in number of hopSize. ARGUMENT:: filterSize @@ -61,11 +61,11 @@ ARGUMENT:: filterSize 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 +ARGUMENT:: windowSize The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As spectral differencing relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As spectral differencing relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 17d1d6b..accd403 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -43,7 +43,7 @@ ARGUMENT:: algorithm ## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications.” QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html :: -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize @@ -80,9 +80,7 @@ Routine{ ) // look at the analysis -c.plot(minval:0, maxval:400) -// plot with a different range to appreciate the confidence: -c.plot(minval:0, maxval:1) +c.plot(separately:true) // The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopSize, then multiply by 2 because of the channel interleaving // here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30. @@ -117,7 +115,7 @@ Routine{ ) // look at the buffer: [pitch,confidence] for left then [pitch,confidence] for right -c.plot(minval:0, maxval:1500) +c.plot(separately:true) :: STRONG::A musical example.:: @@ -151,7 +149,7 @@ d.do({ e.postln; //granulate only the frames that are in our buffer -// We need to convert our indices to frame start. Their position was (index * hopSize) - (winSize) in FluidBufPitch +// We need to convert our indices to frame start. Their position was (index * hopSize) - (windowSize) in FluidBufPitch f = e.collect({arg i; (i * 512) - 1024}); // define a basic grain synth diff --git a/release-packaging/HelpSource/Classes/FluidBufSines.schelp b/release-packaging/HelpSource/Classes/FluidBufSines.schelp index 1ef0f9e..c283211 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSines.schelp @@ -57,7 +57,7 @@ ARGUMENT:: magWeight ARGUMENT:: freqWeight The weight of the frequency proximity of a peak when trying to associate it to an existing track (relative to magWeight - suggested between 0 to 1) -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index e269173..756561b 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -51,7 +51,7 @@ ARGUMENT:: numChans ARGUMENT:: features The destination buffer for the 7 spectral features describing the spectral shape. -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As spectral shape estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize diff --git a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp index 936ec28..e2f3bd6 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp @@ -54,13 +54,13 @@ ARGUMENT:: threshFwd ARGUMENT:: threshBack The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. -ARGUMENT:: winSize +ARGUMENT:: windowSize The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positives. -ARGUMENT:: debounce +ARGUMENT:: clumpLength The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. -ARGUMENT:: minSlice +ARGUMENT:: minSliceLength The minimum duration of a slice in samples. ARGUMENT:: action diff --git a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp index 1fa220e..7773e4c 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp @@ -62,10 +62,10 @@ ARGUMENT:: threshFwd ARGUMENT:: threshBack The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. -ARGUMENT:: winSize +ARGUMENT:: windowSize The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positive. -ARGUMENT:: debounce +ARGUMENT:: clumpLength The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. ARGUMENT:: action @@ -135,7 +135,7 @@ d = Buffer.new(s); e = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s, b, transients: d, residual:e, threshFwd:1.2, debounce:40); + FluidBufTransients.process(s, b, transients: d, residual:e, threshFwd:1.2, clumpLength:40); (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidHPSS.schelp b/release-packaging/HelpSource/Classes/FluidHPSS.schelp index 9fc8aa3..084519f 100644 --- a/release-packaging/HelpSource/Classes/FluidHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidHPSS.schelp @@ -66,11 +66,11 @@ ARGUMENT:: percThreshFreq2 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 +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -85,7 +85,7 @@ ARGUMENT:: maxPercFilterSize How large can the percussive filter be modulated to (percFilterSize), by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - An array of three audio streams: [0] is the harmonic part extracted, [1] is the percussive part extracted, [2] is the rest. The latency between the input and the output is ((harmFilterSize - 1) * hopSize) + 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) + windowSize) samples. Discussion:: HPSS works by using median filters on the spectral magnitudes of a sound. It hinges on a simple modelling assumption that tonal components will tend to yield concentrations of energy across time, spread out in frequency, and percussive components will manifest as concentrations of energy across frequency, spread out in time. By using median filters across time and frequency respectively, we get initial esitmates of the tonal-ness / transient-ness of a point in time and frequency. These are then combined into 'masks' that are applied to the orginal spectral data in order to produce a separation. @@ -112,6 +112,6 @@ b = Buffer.read(s,File.realpath(FluidHPSS.class.filenameSymbol).dirname.withTrai // the residual stream {FluidHPSS.ar(PlayBuf.ar(1,b,loop:1),15,31,2,0.05,40,0.1,-40, 0.1, -10, 0.2, 10)[2].dup}.play -// null test (the process add a latency of ((harmFilterSize - 1) * hopSize) + winSize) samples +// null test (the process add a latency of ((harmFilterSize - 1) * hopSize) + windowSize) samples {var sig = PlayBuf.ar(1,b,loop:1); [FluidHPSS.ar(sig, 17, 31).sum - DelayN.ar(sig, 1, ((((17 - 1) * 512) + 1024) / s.sampleRate))]}.play :: diff --git a/release-packaging/HelpSource/Classes/FluidLoudness.schelp b/release-packaging/HelpSource/Classes/FluidLoudness.schelp index 75d574e..449beb2 100644 --- a/release-packaging/HelpSource/Classes/FluidLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidLoudness.schelp @@ -22,17 +22,17 @@ ARGUMENT:: kWeighting ARGUMENT:: truePeak A flag to switch the computation of TruePeak. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple absolute peak of the frame. -ARGUMENT:: winSize +ARGUMENT:: windowSize The size of the window on which the computation is done. By default 1024 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. ARGUMENT:: hopSize How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. -ARGUMENT:: maxWinSize - How large can the winSize be, by allocating memory at instantiation time. This cannot be modulated. +ARGUMENT:: maxwindowSize + How large can the windowSize be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is winSize. + A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is windowSize. EXAMPLES:: @@ -88,7 +88,7 @@ x.free x = { arg freq=220, type = 1, noise = 0; var source = PinkNoise.ar(noise) + Select.ar(type,[DC.ar(),SinOsc.ar(freq,mul:0.1), VarSaw.ar(freq,mul:0.1), Saw.ar(freq,0.1), Pulse.ar(freq,mul:0.1)]); - Out.kr(b, FluidLoudness.kr(source,winSize:17640,hopSize:4410,maxWinSize:17640)); + Out.kr(b, FluidLoudness.kr(source,windowSize:17640,hopSize:4410,maxwindowSize:17640)); source.dup; }.play; ) diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp index 61e4ed5..54bcb6f 100644 --- a/release-packaging/HelpSource/Classes/FluidMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidMFCC.schelp @@ -31,11 +31,11 @@ ARGUMENT:: maxFreq ARGUMENT:: maxNumCoefs The maximum number of cepstral coefficients that can be computed. This sets the number of channels of the output, and therefore cannot be modulated. -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As MFCC computation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -44,7 +44,7 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A KR signal of STRONG::maxNumCoefs:: channels. The latency is winSize. + A KR signal of STRONG::maxNumCoefs:: channels. The latency is windowSize. EXAMPLES:: diff --git a/release-packaging/HelpSource/Classes/FluidMelBands.schelp b/release-packaging/HelpSource/Classes/FluidMelBands.schelp index 4e3ffbc..e5784ba 100644 --- a/release-packaging/HelpSource/Classes/FluidMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidMelBands.schelp @@ -28,11 +28,11 @@ ARGUMENT:: maxFreq ARGUMENT:: maxNumBands The maximum number of Mel bands that can be modelled. This sets the number of channels of the output, and therefore cannot be modulated. -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -41,7 +41,7 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A KR signal of STRONG::maxNumBands:: channels, giving the measure amplitudes for each band. The latency is winSize. + A KR signal of STRONG::maxNumBands:: channels, giving the measure amplitudes for each band. The latency is windowSize. EXAMPLES:: diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index 5a34698..0493d38 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -6,7 +6,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufNMF, Classe DESCRIPTION:: The FluidNMFFilter object decomposes and resynthesises an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: -It outputs at AR the resynthesis of the best factorisation. The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxRank:: parameter. +It outputs at AR the resynthesis of the best factorisation. The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition (number of components) is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxComponents:: parameter. NMF has been a popular technique in signal processing research for things like source separation and transcription footnote:: Smaragdis and Brown, Non-Negative Matrix Factorization for Polyphonic Music Transcription.::, although its creative potential is so far relatively unexplored. It works iteratively, by trying to find a combination of amplitudes ('activations') that yield the original magnitude spectrogram of the audio input when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results. @@ -20,25 +20,25 @@ FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. f CLASSMETHODS:: METHOD:: ar -The real-time processing method. It takes an audio input, and will yield a audio stream in the form of a multichannel array of size STRONG::maxRank:: . If the bases buffer has fewer than maxRank channels, the remaining outputs will be zeroed. +The real-time processing method. It takes an audio input, and will yield a audio stream in the form of a multichannel array of size STRONG::maxComponents:: . If the bases buffer has fewer than maxComponents channels, the remaining outputs will be zeroed. ARGUMENT:: in The signal input to the factorisation process. ARGUMENT:: bases - The server index of the buffer containing the different bases that the input signal will be matched against. Bases must be STRONG::(fft size / 2) + 1:: frames. If the buffer has more than STRONG::maxRank:: channels, the excess will be ignored. + The server index of the buffer containing the different bases that the input signal will be matched against. Bases must be STRONG::(fft size / 2) + 1:: frames. If the buffer has more than STRONG::maxComponents:: channels, the excess will be ignored. -ARGUMENT::maxRank +ARGUMENT::maxComponents The maximum number of elements the NMF algorithm will try to divide the spectrogram of the source in. This dictates the number of output channels for the ugen. This cannot be modulated. -ARGUMENT:: numIter +ARGUMENT:: iterations The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU intensive, lower numbers will be more unpredictable in quality. -ARGUMENT:: winSize +ARGUMENT:: windowSize The number of samples that are analysed at a time. A lower number yields greater temporal resolution, at the expense of spectral resoultion, and vice-versa. ARGUMENT:: hopSize - The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -79,9 +79,9 @@ d.plot d.play //////(beware !!!! loud!!!) ( -// separate them in 2 ranks +// separate them in 2 components Routine { - FluidBufNMF.process(s, d, bases: e, rank:2); + FluidBufNMF.process(s, d, bases: e, components:2); s.sync; e.query; }.play @@ -111,19 +111,19 @@ e = Buffer.new(s); // train only 2 seconds ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, x, rank:10,fftSize:2048); + FluidBufNMF.process(s,b,0,88200,0,1, c, x, components:10,fftSize:2048); c.query; }.play; ) // wait for the query to print -// then find the rank that has more sustain pitch than pick (TODO: use descriptors with stats) +// then find the component that has more sustain pitch than pick (TODO: use descriptors with stats) ( ~element = 4; {PlayBuf.ar(10,c)[~element]}.play; ) -// copy all the other ranks on itself and the picking basis as the sole component of the 1st channel +// copy all the other components on itself and the picking basis as the sole component of the 1st channel ( Routine{ z = (0..9); @@ -186,7 +186,7 @@ e = { var source = PlayBuf.ar(1,b,loop:1); // after at least 1 second, trigger a first factorisation ( Routine { - FluidBufNMF.process(s, d, bases:c, winSize:2048, rank:3); + FluidBufNMF.process(s, d, bases:c, windowSize:2048, components:3); c.query; }.play; ) @@ -196,19 +196,19 @@ c.plot // then start the splitting effect ( f = {var source = In.ar(0,2); - ReplaceOut.ar(0, Splay.ar(FluidNMFFilter.ar(source.sum, c, 3, winSize:2048))); + ReplaceOut.ar(0, Splay.ar(FluidNMFFilter.ar(source.sum, c, 3, windowSize:2048))); }.play(addAction:\addToTail); ) // kill this boring splitter f.free; -// more fun: processing the 3 rank independently +// more fun: processing the 3 component independently ( f = {arg bases = c.bufnum; var source, x,y,z, rev, dist; source = In.ar(0,2); - #x,y,z = FluidNMFFilter.ar(source.sum, bases, 3, winSize:2048); + #x,y,z = FluidNMFFilter.ar(source.sum, bases, 3, windowSize:2048); rev = FreeVerb.ar(x); dist = (z * 10).atan * 0.1; ReplaceOut.ar(0, Splay.ar([rev,y,dist])); @@ -220,7 +220,7 @@ f.set(\bases, c.bufnum) // here you can retrigger the factorisation g = Buffer.alloc(s,1025,3); -FluidBufNMF.process(s, d, bases:g, winSize:2048, rank:3); +FluidBufNMF.process(s, d, bases:g, windowSize:2048, components:3); f.set(\bases, g.bufnum) //free diff --git a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp index 7953d7b..d2c05c3 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp @@ -6,7 +6,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufNMF, Classe DESCRIPTION:: The FluidNMFMatch object matches an incoming audio signal against a set of spectral templates using an slimmed-down version of Nonnegative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. ‘Learning the Parts of Objects by Non-Negative Matrix Factorization’. Nature 401 (6755): 788–91. https://doi.org/10.1038/44565. :: -It outputs at kr the degree of detected match for each template (the activation amount, in NMF-terms). The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxRank:: parameter. +It outputs at kr the degree of detected match for each template (the activation amount, in NMF-terms). The spectral templates are presumed to have been produced by the offline NMF process (link::Classes/FluidBufNMF::), and must be the correct size with respect to the FFT settings being used (FFT size / 2 + 1 frames long). The rank of the decomposition (number of components) is determined by the number of channels in the supplied buffer of templates, up to a maximum set by the STRONG::maxComponents:: parameter. NMF has been a popular technique in signal processing research for things like source separation and transcription footnote:: Smaragdis and Brown, Non-Negative Matrix Factorization for Polyphonic Music Transcription.::, although its creative potential is so far relatively unexplored. It works iteratively, by trying to find a combination of amplitudes ('activations') that yield the original magnitude spectrogram of the audio input when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results. @@ -20,25 +20,25 @@ 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 bases 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::maxComponents:: . If the bases buffer has fewer than maxComponents channels, the remaining outputs will be zeroed. ARGUMENT:: in The signal input to the factorisation process. ARGUMENT:: bases - The server index of the buffer containing the different bases that the input signal will be matched against. Bases must be STRONG::(fft size / 2) + 1:: frames. If the buffer has more than STRONG::maxRank:: channels, the excess will be ignored. + The server index of the buffer containing the different bases that the input signal will be matched against. Bases must be STRONG::(fft size / 2) + 1:: frames. If the buffer has more than STRONG::maxComponents:: channels, the excess will be ignored. -ARGUMENT::maxRank +ARGUMENT::maxComponents The maximum number of elements the NMF algorithm will try to divide the spectrogram of the source in. This dictates the number of output channelsfor the ugen. This cannot be modulated. -ARGUMENT:: numIter +ARGUMENT:: iterations The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU intensive, lower numbers will be more unpredictable in quality. -ARGUMENT:: winSize +ARGUMENT:: windowSize The number of samples that are analysed at a time. A lower number yields greater temporal resolution, at the expense of spectral resoultion, and vice-versa. ARGUMENT:: hopSize - The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -79,9 +79,9 @@ d.plot d.play //////(beware !!!! loud!!!) ( -// separate them in 2 ranks +// separate them in 2 components Routine { - FluidBufNMF.process(s, d, bases: e, rank:2); + FluidBufNMF.process(s, d, bases: e, components:2); s.sync; e.query; }.play @@ -111,19 +111,19 @@ e = Buffer.new(s); // train only 2 seconds ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, x, rank:10,fftSize:2048); + FluidBufNMF.process(s,b,0,88200,0,1, c, x, components:10,fftSize:2048); c.query; }.play; ) // wait for the query to print -// then find the rank that has the picking sound by changing which channel to listen to +// then find the component that has the picking sound by changing which channel to listen to ( ~element = 6; {PlayBuf.ar(10,c)[~element]}.play; ) -// copy all the other ranks on itself and the picking basis as the sole component of the 1st channel +// copy all the other components on itself and the picking basis as the sole component of the 1st channel ( Routine{ z = (0..9); @@ -135,7 +135,7 @@ Routine{ e.plot; -//using this trained basis we can see the envelop (activations) of each rank +//using this trained basis we can see the envelop (activations) of each component {FluidNMFMatch.kr(PlayBuf.ar(1,b),e,2,fftSize:2048)}.plot(1); // the left/top activations are before, the pick before the sustain. @@ -187,13 +187,13 @@ e = Buffer.new(s); // train where all objects are present ( Routine { - FluidBufNMF.process(s,b,130000,150000,0,1, c, x, rank:10); + FluidBufNMF.process(s,b,130000,150000,0,1, c, x, components:10); c.query; }.play; ) // wait for the query to print -// then find a rank for each item you want to find. You could also sum them. Try to find a rank with a good object-to-rest ratio +// then find a component for each item you want to find. You could also sum them. Try to find a component with a good object-to-rest ratio ( ~dog =2; {PlayBuf.ar(10,c)[~dog]}.play @@ -205,7 +205,7 @@ Routine { ) -// copy at least one other rank to a third rank, a sort of left-over channel +// copy at least one other component to a third filter, a sort of left-over channel ( Routine{ FluidBufCompose.process(s, x, startChan:~dog, numChans: 1, destination: e); @@ -306,4 +306,3 @@ c.query CODE:: //to be completed :: - \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp index 29ae2ee..449c05f 100644 --- a/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidOnsetSlice.schelp @@ -35,7 +35,7 @@ ARGUMENT:: function ARGUMENT:: threshold The thresholding of a new slice. Value ranges are different for each function, from 0 upwards. -ARGUMENT:: debounce +ARGUMENT:: minSliceLength The minimum duration of a slice in number of hopSize. ARGUMENT:: filterSize @@ -44,11 +44,11 @@ ARGUMENT:: filterSize 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 +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -57,7 +57,7 @@ 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 at maximum. + An audio stream with impulses at detected transients. The latency between the input and the output is windowSize at maximum. EXAMPLES:: @@ -65,7 +65,7 @@ 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 +// basic param (the process add a latency of windowSize samples {var sig = PlayBuf.ar(1,b,loop:1); [FluidOnsetSlice.ar(sig) * 0.5, DelayN.ar(sig, 1, 1024/ s.sampleRate)]}.play // other parameters @@ -82,4 +82,4 @@ b = Buffer.read(s,File.realpath(FluidOnsetSlice.class.filenameSymbol).dirname.wi Pan2.ar(syncd,pan); }.play ) -:: \ No newline at end of file +:: diff --git a/release-packaging/HelpSource/Classes/FluidPitch.schelp b/release-packaging/HelpSource/Classes/FluidPitch.schelp index 9cb1f90..af6a0f1 100644 --- a/release-packaging/HelpSource/Classes/FluidPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidPitch.schelp @@ -24,11 +24,11 @@ ARGUMENT:: algorithm ## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications.” QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html :: -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -37,7 +37,7 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is winSize. + A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is windowSize. EXAMPLES:: @@ -103,7 +103,7 @@ x.set(\type, 3) // adding noise shows the comparative sturdiness of the spectral pitch tracker x.set(\noise, 0.05) -//if latency is no issue, getting a higher winSize will stabilise the algorithm even more +//if latency is no issue, getting a higher windowSize will stabilise the algorithm even more :: STRONG::a more musical example:: diff --git a/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp b/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp index 2a29a35..2f73b18 100644 --- a/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp +++ b/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp @@ -16,11 +16,11 @@ METHOD:: ar ARGUMENT:: in The input to be passed-through -ARGUMENT:: winSize +ARGUMENT:: windowSize The size of the buffered window to be analysed, in samples. It will add that much latency to the signal. ARGUMENT:: hopSize - How much the buffered window moves forward, in samples. The -1 default value will default to half of winSize (overlap of 2). + How much the buffered window moves forward, in samples. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize How large will the FFT be, zero-padding the buffer to the right size, which should be bigger than the windowSize argument, bigger than 4 samples, and should be a power of 2. This is a way to oversample the FFT for extra precision. The -1 default value will default to windowSize. @@ -30,7 +30,7 @@ ARGUMENT:: maxFFTSize RETURNS:: - Same as input, delayed by the winSize. + Same as input, delayed by the windowSize. EXAMPLES:: diff --git a/release-packaging/HelpSource/Classes/FluidSines.schelp b/release-packaging/HelpSource/Classes/FluidSines.schelp index 83010a7..fd6152f 100644 --- a/release-packaging/HelpSource/Classes/FluidSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidSines.schelp @@ -36,11 +36,11 @@ ARGUMENT:: magWeight ARGUMENT:: freqWeight The weight of the frequency proximity of a peak when trying to associate it to an existing track (relative to magWeight - suggested between 0 to 1) -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -49,7 +49,7 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - An array of two audio streams: [0] is the harmonic part extracted, [1] is the rest. The latency between the input and the output is (( hopSize * minTrackLen) + winSize) samples. + An array of two audio streams: [0] is the harmonic part extracted, [1] is the rest. The latency between the input and the output is (( hopSize * minTrackLen) + windowSize) samples. EXAMPLES:: @@ -59,11 +59,11 @@ 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,loop:1),threshold: 0.2, minTrackLen: 2, winSize: 2048, fftSize: 8192)}.play +{FluidSines.ar(PlayBuf.ar(1,b,loop:1),threshold: 0.2, minTrackLen: 2, windowSize: 2048, fftSize: 8192)}.play // interactive parameters with a narrower bandwidth -{FluidSines.ar(PlayBuf.ar(1,b,loop:1),30,MouseX.kr(), 5, winSize: 1000, hopSize: 200, fftSize: 4096)}.play +{FluidSines.ar(PlayBuf.ar(1,b,loop:1),30,MouseX.kr(), 5, windowSize: 1000, hopSize: 200, fftSize: 4096)}.play -// null test (the process add a latency of (( hopSize * minTrackLen) + winSize) samples +// null test (the process add a latency of (( hopSize * minTrackLen) + windowSize) samples {var sig = PlayBuf.ar(1,b,loop:1); [FluidSines.ar(sig).sum - DelayN.ar(sig, 1, ((( 512 * 15) + 1024)/ s.sampleRate))]}.play :: diff --git a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp index 6985267..6376579 100644 --- a/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidSpectralShape.schelp @@ -32,11 +32,11 @@ METHOD:: kr ARGUMENT:: in The audio to be processed. -ARGUMENT:: winSize +ARGUMENT:: windowSize The window size. As spectral shape estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -45,7 +45,7 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A 7-channel KR signal with the seven spectral shape descriptors. The latency is winSize. + A 7-channel KR signal with the seven spectral shape descriptors. The latency is windowSize. EXAMPLES:: diff --git a/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp index 8f58206..1df863d 100644 --- a/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp @@ -34,13 +34,13 @@ ARGUMENT:: threshFwd ARGUMENT:: threshBack The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. -ARGUMENT:: winSize +ARGUMENT:: windowSize The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positives. -ARGUMENT:: debounce +ARGUMENT:: clumpLength The window size in sample within with positive detections will be clumped together to avoid overdetecting in time. -ARGUMENT:: minSlice +ARGUMENT:: minSliceLength The minimum duration of a slice in samples. RETURNS:: @@ -57,14 +57,14 @@ b = Buffer.read(s,File.realpath(FluidTransientSlice.class.filenameSymbol).dirnam {var sig = PlayBuf.ar(1,b,loop:1); [FluidTransientSlice.ar(sig) * 0.5, DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate))]}.play // other parameters -{var sig = PlayBuf.ar(1,b,loop:1); [FluidTransientSlice.ar(sig,order:80,minSlice:2205) * 0.5, DelayN.ar(sig, 1, ((256 + 128 - 80)/ s.sampleRate))]}.play +{var sig = PlayBuf.ar(1,b,loop:1); [FluidTransientSlice.ar(sig,order:80,minSliceLength:2205) * 0.5, DelayN.ar(sig, 1, ((256 + 128 - 80)/ s.sampleRate))]}.play // more musical trans-trigged autopan ( { var sig, trig, syncd, pan; sig = PlayBuf.ar(1,b,loop:1); - trig = FluidTransientSlice.ar(sig,order:10,minSlice:4410); + trig = FluidTransientSlice.ar(sig,order:10,minSliceLength:4410); syncd = DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate)); pan = TRand.ar(-1,1,trig); Pan2.ar(syncd,pan); diff --git a/release-packaging/HelpSource/Classes/FluidTransients.schelp b/release-packaging/HelpSource/Classes/FluidTransients.schelp index 7173d3e..8724858 100644 --- a/release-packaging/HelpSource/Classes/FluidTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidTransients.schelp @@ -39,10 +39,10 @@ ARGUMENT:: threshFwd ARGUMENT:: threshBack The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. -ARGUMENT:: winSize +ARGUMENT:: windowSize The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positive. -ARGUMENT:: debounce +ARGUMENT:: clumpLength The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. RETURNS:: diff --git a/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc b/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc index dfb6b1f..79c2ecb 100644 --- a/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc +++ b/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc @@ -1,7 +1,7 @@ //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; +var fileNames; c = [0]; FileDialog.new({|selection| @@ -21,13 +21,16 @@ FileDialog.new({|selection| maxchans = maxchans.max(file.numChannels); }); }); + c.postln; + totaldur.postln; + maxchans.postln; 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);}); + Buffer.read(s, f.asAbsolutePath,action:{arg tempbuf; FluidBufCompose.process(s,tempbuf,destination:b,destStartFrame:c[i],action:{tempbuf.free});}); s.sync; }; ("loading buffers done in" + (Main.elapsedTime - t).round(0.1) + "seconds.").postln; diff --git a/release-packaging/ignore/Examples/nmf/JiT-NMF.scd b/release-packaging/ignore/Examples/nmf/JiT-NMF.scd index bc81aa4..7ec4394 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, numFrames: 22500, destination: c.bufnum, rank: 3, fftSize: 1024, winSize: 512, hopSize: 256); + FluidBufNMF.process(s, b, numFrames: 22500, destination: c.bufnum, iterations: 3, fftSize: 1024, windowSize: 512, hopSize: 256); }, { - FluidBufNMF.process(s, b, 22050, 22500, destination: d.bufnum, rank: 3, fftSize: 1024, winSize: 512, hopSize: 256); + FluidBufNMF.process(s, b, 22050, 22500, destination: d.bufnum, iterations: 3, fftSize: 1024, windowSize: 512, hopSize: 256); });}, '/processplease', s.addr); ) From cfc6c868bd2009c5bd7aa9b36bb792cf446690d0 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 4 Jun 2019 18:09:47 -0300 Subject: [PATCH 49/50] typos found in the interface change. --- release-packaging/Classes/FluidBufMFCC.sc | 6 +++--- release-packaging/Classes/FluidMFCC.sc | 6 +++--- .../HelpSource/Classes/FluidBufMFCC.schelp | 10 +++++----- .../HelpSource/Classes/FluidMFCC.schelp | 14 +++++++------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index 5614dc2..131ae3c 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -1,5 +1,5 @@ FluidBufMFCC{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -13,10 +13,10 @@ FluidBufMFCC{ //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) - // same goes to maxNumCoefs, which is passed numCoefs in this case + // same goes to maxNumCoeffs, which is passed numCoeffs in this case forkIfNeeded{ - server.sendMsg(\cmd, \BufMFCC, source, startFrame, numFrames, startChan, numChans, features, numCoefs, numBands, minFreq, maxFreq, numCoefs, windowSize, hopSize, fftSize, maxFFTSize); + server.sendMsg(\cmd, \BufMFCC, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, maxFFTSize); server.sync; features = server.cachedBufferAt(features); features.updateInfo; server.sync; action.value(features); diff --git a/release-packaging/Classes/FluidMFCC.sc b/release-packaging/Classes/FluidMFCC.sc index 3c06371..76d37b6 100644 --- a/release-packaging/Classes/FluidMFCC.sc +++ b/release-packaging/Classes/FluidMFCC.sc @@ -1,7 +1,7 @@ FluidMFCC : MultiOutUGen { - *kr { arg in = 0, numCoefs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumCoefs = 40, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; - ^this.multiNew('control', in.asAudioRateInput(this), numCoefs, numBands, minFreq, maxFreq, maxNumCoefs, windowSize, hopSize, fftSize, maxFFTSize); + *kr { arg in = 0, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumCoeffs = 40, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; + ^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, numBands, minFreq, maxFreq, maxNumCoeffs, windowSize, hopSize, fftSize, maxFFTSize); } init {arg ...theInputs; @@ -11,7 +11,7 @@ FluidMFCC : MultiOutUGen { checkInputs { if(inputs.at(5).rate != 'scalar') { - ^(": maxNumCoefs cannot be modulated."); + ^(": maxNumCoeffs cannot be modulated."); }; if(inputs.at(9).rate != 'scalar') { ^(": maxFFTSize cannot be modulated."); diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp index e2aabf4..3228b5b 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -4,9 +4,9 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufMelBands DESCRIPTION:: -This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoefs:: number of cepstral coefficients. It has the avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: +This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoeffs:: number of cepstral coefficients. It has the avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: -The process will return a single multichannel buffer of STRONG::numCoefs:: per input channel. Each frame represents a value, which is every hopSize. +The process will return a single multichannel buffer of STRONG::numCoeffs:: per input channel. Each frame represents a value, which is every hopSize. CLASSMETHODS:: @@ -32,9 +32,9 @@ ARGUMENT:: numChans For multichannel srcBuf, how many channel should be processed. ARGUMENT:: features - The destination buffer for the numCoefs coefficients describing the spectral shape. + The destination buffer for the numCoeffs coefficients describing the spectral shape. -ARGUMENT:: numCoefs +ARGUMENT:: numCoeffs The number of cepstral coefficients to be outputed. It will decide how many channels are produce per channel of the source. ARGUMENT:: numBands @@ -104,7 +104,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMFCC.process(s, b, numCoefs:5, features: c); + FluidBufMFCC.process(s, b, numCoeffs:5, features: c); (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp index 54bcb6f..8bedc57 100644 --- a/release-packaging/HelpSource/Classes/FluidMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidMFCC.schelp @@ -4,9 +4,9 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMelBands DESCRIPTION:: -This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoefs:: number of cepstral coefficients. It has the avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: +This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoeffs:: number of cepstral coefficients. It has the avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: -The process will return a multichannel control steam of STRONG::maxNumCoefs::, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. +The process will return a multichannel control steam of STRONG::maxNumCoeffs::, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. CLASSMETHODS:: @@ -16,8 +16,8 @@ METHOD:: kr ARGUMENT:: in The audio to be processed. -ARGUMENT:: numCoefs - The number of cepstral coefficients to be outputed. It is limited by the maxNumCoefs parameter. When the number is smaller than the maximum, the output is zero-padded. +ARGUMENT:: numCoeffs + The number of cepstral coefficients to be outputed. It is limited by the maxNumCoeffs parameter. When the number is smaller than the maximum, the output is zero-padded. ARGUMENT:: numBands The number of bands that will be perceptually equally distributed between minFreq and maxFreq to describe the spectral shape before it is converted to cepstral coefficients. @@ -28,7 +28,7 @@ ARGUMENT:: minFreq ARGUMENT:: maxFreq The highest boundary of the highest band of the model, in Hz. -ARGUMENT:: maxNumCoefs +ARGUMENT:: maxNumCoeffs The maximum number of cepstral coefficients that can be computed. This sets the number of channels of the output, and therefore cannot be modulated. ARGUMENT:: windowSize @@ -44,7 +44,7 @@ ARGUMENT:: maxFFTSize How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A KR signal of STRONG::maxNumCoefs:: channels. The latency is windowSize. + A KR signal of STRONG::maxNumCoeffs:: channels. The latency is windowSize. EXAMPLES:: @@ -80,7 +80,7 @@ r = Routine { ( x = {arg type = 0; var source = Select.ar(type,[SinOsc.ar(220),Saw.ar(220),Pulse.ar(220)]) * LFTri.kr(0.1).exprange(0.01,0.1); - Out.kr(b,FluidMFCC.kr(source,maxNumCoefs:13)); + Out.kr(b,FluidMFCC.kr(source,maxNumCoeffs:13)); source.dup; }.play; ) From be46f346f37e98953d4f67da6ed03522d047f330 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 4 Jun 2019 18:10:45 -0300 Subject: [PATCH 50/50] new interface on (buf)RTNoveltySlice in progress (for alpha08) --- release-packaging/Classes/FluidBufRTNoveltySlice.sc | 6 +++--- release-packaging/Classes/FluidRTNoveltySlice.sc | 4 ++-- .../HelpSource/Classes/FluidBufRTNoveltySlice.schelp | 2 +- .../HelpSource/Classes/FluidRTNoveltySlice.schelp | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/release-packaging/Classes/FluidBufRTNoveltySlice.sc b/release-packaging/Classes/FluidBufRTNoveltySlice.sc index 104f034..5741f15 100644 --- a/release-packaging/Classes/FluidBufRTNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufRTNoveltySlice.sc @@ -1,7 +1,7 @@ FluidBufRTNoveltySlice{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, winSize = 1024, hopSize = -1, fftSize = -1, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action; - var maxFFTSize = if (fftSize == -1) {winSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; indices = indices.asUGenInput; @@ -12,7 +12,7 @@ FluidBufRTNoveltySlice{ server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufRTNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, winSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize); + server.sendMsg(\cmd, \BufRTNoveltySlice, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize); server.sync; indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; action.value(indices); diff --git a/release-packaging/Classes/FluidRTNoveltySlice.sc b/release-packaging/Classes/FluidRTNoveltySlice.sc index b908004..813d15a 100644 --- a/release-packaging/Classes/FluidRTNoveltySlice.sc +++ b/release-packaging/Classes/FluidRTNoveltySlice.sc @@ -1,6 +1,6 @@ FluidRTNoveltySlice : UGen { - *ar { arg in = 0, feature = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, winSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384, maxKernelSize = 101, maxFilterSize = 100; - ^this.multiNew('audio', in.asAudioRateInput(this), feature, kernelSize, threshold, filterSize, winSize, hopSize, fftSize, maxFFTSize, maxKernelSize, maxFilterSize) + *ar { arg in = 0, feature = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384, maxKernelSize = 101, maxFilterSize = 100; + ^this.multiNew('audio', in.asAudioRateInput(this), feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, maxFFTSize, maxKernelSize, maxFilterSize) } checkInputs { if(inputs.at(8).rate != 'scalar') { diff --git a/release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp index 586e586..e90651f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufRTNoveltySlice.schelp @@ -53,7 +53,7 @@ ARGUMENT:: threshold 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 +ARGUMENT:: windowSize The window size. As novelty estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize diff --git a/release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp index 58304bc..166cf76 100644 --- a/release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidRTNoveltySlice.schelp @@ -34,11 +34,11 @@ ARGUMENT:: threshold 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 +ARGUMENT:: windowSize The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of winSize (overlap of 2). + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to windowSize. @@ -53,7 +53,7 @@ ARGUMENT:: maxFilterSize This cannot be modulated. RETURNS:: - An audio stream with impulses at detected transients. The latency between the input and the output is winSize at maximum. + An audio stream with impulses at detected transients. The latency between the input and the output is windowSize at maximum. EXAMPLES:: @@ -61,7 +61,7 @@ code:: //load some sounds b = Buffer.read(s,File.realpath(FluidRTNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); -// basic param (the process add a latency of winSize samples +// basic param (the process add a latency of windowSize samples {var sig = PlayBuf.ar(1,b,loop:1); [FluidRTNoveltySlice.ar(sig,0,3,0.2) * 0.5, DelayN.ar(sig, 1, 1024/ s.sampleRate)]}.play // other parameters