Merge branch 'master' into clients/inter_client_comms

nix
Owen Green 6 years ago
commit c84a81a3bf

4
.gitignore vendored

@ -5,3 +5,7 @@ release-packaging/Plugins
release-packaging/AudioFiles release-packaging/AudioFiles
*.scx *.scx
.vs/ .vs/
Darwin/*
Linux/*
Windows/*
install/

@ -1,22 +1,37 @@
####### original SC Cmake file starts here # Copyright 2017-2019 University of Huddersfield.
cmake_minimum_required(VERSION 3.3) # Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) ################################################################################
set(CMAKE_CXX_STANDARD 14) # Paths
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install" CACHE PATH "")
if(APPLE) set(SC_LIBRARY_OUTPUT_PREFIX "release-packaging" CACHE STRING "Where in the hierarchy to write sc plugins (different for superbuild)")
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(SC_PATH "" CACHE PATH "Path to the top of SuperCollider source tree")
SET(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") if(NOT SC_PATH)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7" CACHE STRING "Minimum OS X deployment version") message(FATAL_ERROR "SuperCollider source path is not set")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
endif() endif()
project (fluid_decomposition_supercollider LANGUAGES CXX) set(FLUID_PATH "" CACHE PATH "Optional path to the Fluid Decomposition repo")
set(FLUID_M_PATH "" CACHE PATH "Optional path to the Fluid fluid_manipulation repo")
option(SUPERNOVA "Build plugins for supernova" OFF) if (APPLE)
set(CMAKE_OSX_ARCHITECTURES x86_64)
set(CMAKE_XCODE_GENERATE_SCHEME ON)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
endif()
################################################################################
# Main project
project (fluid_decomposition_supercollider LANGUAGES CXX)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY ON)
MACRO(SUBDIRLIST result curdir) MACRO(SUBDIRLIST result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
@ -29,66 +44,57 @@ MACRO(SUBDIRLIST result curdir)
SET(${result} ${dirlist}) SET(${result} ${dirlist})
ENDMACRO() ENDMACRO()
set(FLUID_PATH ~/fluid_decomposition CACHE PATH "The top level of the fluid_decomposition repo") include(FetchContent)
set(LOCAL_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(FLUID_M_PATH ~/dev/fluid_manipulation/fluid_manipulation CACHE PATH "The top level of the fluid_manipulation repo")
set(FETCHCONTENT_QUIET FALSE)
get_filename_component(FLUID_ABS_PATH "${FLUID_PATH}" ABSOLUTE) FetchContent_Declare(
message("${FLUID_ABS_PATH}") fluid_decomposition
# if (NOT DEFINED ${FLUID_DECOMP_PATH}) GIT_REPOSITORY https://bitbucket.org/flucoma/fluid_decomposition.git
# message(FATAL_ERROR "Please set the path to the fluid_decomposition sources with -DFLUID_DECOMP_PATH=<path>") GIT_PROGRESS TRUE
# endif() )
if (NOT (EXISTS "${FLUID_ABS_PATH}/build/fluid_decomposition-exports.cmake")) if(FLUID_PATH)
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") get_filename_component(
FETCHCONTENT_SOURCE_DIR_FLUID_DECOMPOSITION ${FLUID_PATH} ABSOLUTE
)
endif() endif()
if (NOT (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/AudioFiles/")) FetchContent_GetProperties(fluid_decomposition)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/AudioFiles/") if(NOT fluid_decomposition_POPULATED)
FetchContent_Populate(fluid_decomposition)
add_subdirectory(${fluid_decomposition_SOURCE_DIR} ${fluid_decomposition_BINARY_DIR})
include(flucoma_version)
include(flucoma-buildtools)
endif() endif()
file(COPY "${FLUID_ABS_PATH}/AudioFiles/" DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/AudioFiles/") set_if_toplevel(VAR CMAKE_LIBRARY_OUTPUT_DIRECTORY
TOPLEVEL "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/plugins"
SUPERBUILD "${CMAKE_SOURCE_DIR}/sc_plugins/${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR}")
include("${FLUID_ABS_PATH}/build/fluid_decomposition-exports.cmake")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/plugins")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
set(CMAKE_SHARED_MODULE_PREFIX "") set(CMAKE_SHARED_MODULE_PREFIX "")
if(APPLE OR WIN32) if(APPLE OR WIN32)
set(CMAKE_SHARED_MODULE_SUFFIX ".scx") set(CMAKE_SHARED_MODULE_SUFFIX ".scx")
endif() 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}
)
if(WIN32)
target_compile_options(
FFTLIB PRIVATE $<$<NOT:$<CONFIG:DEBUG>>: /arch:AVX>
)
else(WIN32)
target_compile_options(
FFTLIB PRIVATE $<$<NOT:$<CONFIG:DEBUG>>: -mavx -msse -msse2 -msse3 -msse4>
)
endif(WIN32)
#needed for complaint-free static linking with GCC #needed for complaint-free static linking with GCC
if(CMAKE_COMPILER_IS_GNUCXX) if(CMAKE_COMPILER_IS_GNUCXX)
target_compile_options( FFTLIB PUBLIC -fPIC ) target_compile_options( HISSTools_FFT PUBLIC -fPIC )
ENDIF(CMAKE_COMPILER_IS_GNUCXX) ENDIF()
add_library(FLUID_SC_WRAPPER INTERFACE) add_library(FLUID_SC_WRAPPER INTERFACE)
target_include_directories(FLUID_SC_WRAPPER
INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include/"
)
target_sources(FLUID_SC_WRAPPER target_sources(FLUID_SC_WRAPPER
INTERFACE INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include/FluidSCWrapper.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/FluidSCWrapper.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/SCBufferAdaptor.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/SCBufferAdaptor.hpp"
) )
SUBDIRLIST(PROJECT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src") SUBDIRLIST(PROJECT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src")
@ -98,3 +104,18 @@ foreach (project_dir ${PROJECT_DIRS})
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/${project_dir}") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/${project_dir}")
endif () endif ()
endforeach () endforeach ()
#install bits.
set(SC_INSTALL_PREFIX "." CACHE PATH "Prefix for assembling SC packages")
set(FLUID_PACKAGE_NAME FluidCorpusManipulation CACHE STRING "Name for published package")
set(SC_PACKAGE_ROOT ${SC_INSTALL_PREFIX}/${FLUID_PACKAGE_NAME})
foreach(PACKAGE_DIRECTORY Classes;HelpSource;Examples)
install(DIRECTORY "release-packaging/${PACKAGE_DIRECTORY}" DESTINATION ${SC_PACKAGE_ROOT})
endforeach()
install(DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ DESTINATION ${SC_PACKAGE_ROOT}/plugins PATTERN "*.ilk" EXCLUDE PATTERN "*.PDB" EXCLUDE)
install(DIRECTORY "${fluid_decomposition_SOURCE_DIR}/AudioFiles" DESTINATION ${SC_PACKAGE_ROOT})
install(FILES QuickStart.md DESTINATION ${SC_PACKAGE_ROOT})
install(FILES ${fluid_decomposition_SOURCE_DIR}/license-executableform.md DESTINATION ${SC_PACKAGE_ROOT} RENAME license.md)

@ -1,9 +1,11 @@
# Instructions for the SupperCollider version of the fluid.decomposition toolbox # Instructions for the SuperCollider version of the Fluid Corpus Manipulation toolbox
## How to start: ## How to start:
1) move the full fluid_decomposition folder in your Extensions folder (see the 'Using Extensions' Guide in the documentation), and restart SuperCollider. 1) move the full FluidCorpusManipulation folder in your Extensions folder (see the 'Using Extensions' Guide in the documentation), and restart SuperCollider.
There is a Guide giving the overview of the toolbox. Full documentation, and examples, are also available. There is a Guide giving the overview of the toolbox. Full documentation is also available, as well as an Examples folder in the package.
#### Enjoy! #### Enjoy!
> This project has received funding from the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).

@ -0,0 +1,132 @@
# Fluid Corpus Manipulation: SuperCollider Objects Library
This repository hosts code for generating the SC objects and documentation resources for the Fluid Corpus Manipulation Project. Much of the actual code that does the exciting stuff lives in this repository's principal dependency, the [Fluid Corpus Manipulation Library](). What lives here are:
* A wrapper from our code to the SC API, that allows us to generate SC objects from a generic class.
* Stubs for producing SC objects for each 'client' in the Fluid Corpus Manipulation Library.
* CMake code for managing dependencies, building and packaging.
## I'm in a Hurry...
...and you already have a development environment set up, understand CMake, and have the SC source available?
Cool:
```
cmake -DSC_PATH=<location of your SC source> ..
make
```
You can either symbolically link the `release-packaging` directory into your SC extensions folder, or use CMake to generate a 'clean package' with `make install`.
## Pre-requisites
* [CMake](http://cmake.org) >= 3.11
* A C++ 14 compliant compiler for Mac or Windows (via XCode tools on Mac, and Visual Studio 17 >= 15.9 on Windows)
## Dependencies
* [SC Source Code](https://github.com/supercollider/supercollider): this is the only dependency we don't (optionally) manage for you, so there must be a version available to point to when you run, using the CMake Variable `SC_PATH` (see below). It can live anywhere on your file system.
These will be downloaded and configured automatically, unless you pass CMake a source code location on disk for each (see below):
* [Fluid Corpus Manipulation Library]()
* [Eigen](https://gitlab.com/libeigen/eigen) (3.3.5)
* [HISSTools Library](https://github.com/AlexHarker/HISSTools_Library)
Unless you are simultaneously committing changes to these dependencies (or are *seriously* worried about disk space), there is nothing to be gained by pointing to external copies, and the easiest thing to is let CMake handle it all.
## Building
Simplest possible build:
1. Download the SuperCollider source (>= 3.10.0) if you haven't already
2. Clone this repo
```
git clone <whereis this>
```
3. Change to the directory for this repo, make a build directory, and run CMake, passing in the location for the Max SDK
```
mkdir -p build && cd build
cmake -DSC_PATH=<location of your SC source> ..
```
At this point, CMake will set up your tool chain (i.e. look for compilers), and download all the dependencies.
An alternative to setting up / running CMake directly on the command line is to install the CMake GUI, or use to use the curses gui `ccmake`.
With CMake you have a choice of which build system you use.
* The default on Mac is `Unix Makefiles`, but you can use Xcode by passing `-GXcode` to CMake when you first run it.
* The default on Windows is the latest version of Visual Studio installed. However, Visual Studio can open CMake files directly as projects, which has some upsides. When used this way, CMake variables have to be set via a JSON file that MSVC will use to configure CMake.
When using `make`, then
```
make
```
will compile all objects ('targets', in CMake-ish). Alternatively, in Xcode or Visual Studio, running 'build' will (by default) build all objects. Note that these IDEs allow you to build both for debugging and for release, whereas with Makefiles, you need to re-run CMake and pass a `CMAKE_CONFIG` variable for different build types.
```
make install
```
Will assemble a clean package in `release-packaging/FluidCorpusManipulation`.
## Structure:
The top-level folders mostly correspond to those you would find in a typical Max package. Some more will appear when you compile (such as `externals` and possibly `docs`).
The main action is in `source`:
```
source
├── include
├── src
└── scripts
```
* `include` contains the header files for the FluCoMa-SC wrapper
* `src` contains a subfolder for each object to be generated; each of these subfolders has a `.cpp` stub and a `CMakeLists.txt`. The `.cpp` file name needs to match its parent folder.
* `scripts` contains CMake scripts (most significantly, `target_post.cmake`, which sets behaviours for all the objects).
## Development: Manual Dependencies
If you're making changes to the Fluid Corpus Manipulation Library and testing against Max (and, hopefully, our other deployment environments), then it makes sense to have the source for this cloned somewhere else on your disk, so that you can commit and push changes, and ensure that they work in all environments. This would be the case, e.g., if you're developing a new client.
To bypass the automatic cloning of the Fluid Corpus Manipulation Library, we pass in the cache variable `FLUID_PATH`
```
cmake -DSC_PATH=<location of your SC source> -DFLUID_PATH=<location of Fluid Corpus Manipulation Library> ..
```
Note
1. You don't need to run CMake on the Fluid Corpus Manipulation Library first (well, you can, but it doesn't make any difference!). CMake's FetchContent facility will grab the targets from there, but won't look in its CMakeCache, so there should never be a conflict between the state of a build tree at `FLUID_PATH` and your build-tree here.
2. It is **up to you** to make sure the commits you have checked out in each repository make sense together. We use tags against release versions on the `master` branch, so  at the very least  these should line up (unless you're tracking down some regression bug or similar). In general, there is no guarantee, or likelihood, that mismatched tags will build or run, as architectural changes can, do, will happen...
### Dependencies of dependencies!
The same steps and considerations apply to manually managing the dependencies of the Fluid Corpus Manipulation Library itself. If these aren't explicitly passed whilst running CMake against this build tree, then CMake will download them itself against the tags / commits we develop against. Nevertheless, if you are in the process of making changes to these libraries and running against this (which is much less likely than above), then the CMake variables of interest are:
* `EIGEN_PATH` pointing to the location of Eigen on disk
* `HISS_PATH` pointing to the location of the HISSTools Library
To find out which branches / tags / commits of these we use, look in the top level `CMakeLists.txt` of the Fluid Corpus Manipulation Library for the `FetchContent_Declare` statements for each dependency.
# Vector instructions and CPU architecture.
Most types of CPU in common use support a range of specialised instructions for processing vectors of numbers at once, which can offer substantial speed gains. For instance, on Intel / AMD chips there have been a succession of such instruction sets (MMX, SSE, AVX and so on).
If you find objects causing 'illegal instruction' segmentation faults, it is likely that our default vector instruction flag isn't supported by your CPU.
### I'm in a hurry...
And *only building for your own machine*? You want to enable the maximal set of CPU features for your machine without worrying? Using Clang / GCC?
Pass `-DFLUID_ARCH=-mnative` when you run CMake. This tells the compiler to figure out what instruction sets your personal CPU supports, and enable them. This implies a possible performance gain in return for a portability loss.
### More Detail
By default, on Intel / AMD, we enable AVX instructions (`-mavx` on clang/GCC; `/ARCH:AVX` on MSVC). These should work on all relatively recent CPUs (from, say, 2012 onwards). On Arm (with the Bela in mind), we use the following architecture flags:
```
-march=armv7-a -mtune=cortex-a8 -mfloat-abi=hard -mfpu=neon
```
If your needs are different, then these defaults can be overridden by passing the desired compiler flags to CMake via the `FLUID_ARCH` cache variable. Note that this variable expects you to pass arguments in a form suitable for your compiler, and currently performs no checking or validation.
* Clang and GCC options are generally the same, see https://clang.llvm.org/docs/ClangCommandLineReference.html
* See MSVC options here https://docs.microsoft.com/en-us/cpp/build/reference/arch-minimum-cpu-architecture?view=vs-2019
If your Intel / AMD chip is too old to support AVX, it probably still supports SSE. On Mac OS and Linux, `sysctl -a | grep cpu.feat` can be used to produce a list of the various features your CPU supports.
> This project has received funding from the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).

@ -1,3 +1,13 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#pragma once #pragma once
#include "SCBufferAdaptor.hpp" #include "SCBufferAdaptor.hpp"
@ -5,9 +15,8 @@
#include <clients/common/Result.hpp> #include <clients/common/Result.hpp>
#include <data/FluidTensor.hpp> #include <data/FluidTensor.hpp>
#include <data/TensorTypes.hpp> #include <data/TensorTypes.hpp>
#include <FluidVersion.hpp>
#include <SC_PlugIn.hpp> #include <SC_PlugIn.hpp>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <tuple> #include <tuple>
@ -47,17 +56,14 @@ namespace impl {
// Iterate over kr/ir inputs via callbacks from params object // Iterate over kr/ir inputs via callbacks from params object
struct FloatControlsIter struct FloatControlsIter
{ {
FloatControlsIter(float **vals, size_t N) FloatControlsIter(float** vals, index N) : mValues(vals), mSize(N) {}
: mValues(vals)
, mSize(N)
{}
float next() { return mCount >= mSize ? 0 : *mValues[mCount++]; } float next() { return mCount >= mSize ? 0 : *mValues[mCount++]; }
void reset(float **vals) void reset(float** vals)
{ {
mValues = vals; mValues = vals;
mCount = 0; mCount = 0;
} }
size_t size() const noexcept { return mSize; } size_t size() const noexcept { return mSize; }
@ -66,12 +72,12 @@ struct FloatControlsIter
return mSize - mCount; return mSize - mCount;
} }
private: private:
float **mValues; float** mValues;
size_t mSize; index mSize;
size_t mCount{0}; index mCount{0};
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Real Time Processor // Real Time Processor
@ -81,56 +87,75 @@ class RealTime : public SCUnit
using HostVector = FluidTensorView<float, 1>; using HostVector = FluidTensorView<float, 1>;
using ParamSetType = typename Client::ParamSetType; using ParamSetType = typename Client::ParamSetType;
// using Client = typename Wrapper::ClientType;
public: public:
static void setup(InterfaceTable *ft, const char *name) static void setup(InterfaceTable* ft, const char* name)
{ {
registerUnit<Wrapper>(ft, name); registerUnit<Wrapper>(ft, name);
ft->fDefineUnitCmd(name, "latency", doLatency); ft->fDefineUnitCmd(name, "latency", doLatency);
} }
static void doLatency(Unit *unit, sc_msg_iter*) static void doLatency(Unit* unit, sc_msg_iter*)
{ {
float l[]{static_cast<float>(static_cast<Wrapper *>(unit)->mClient.latency())}; float l[]{
auto ft = Wrapper::getInterfaceTable(); static_cast<float>(static_cast<Wrapper*>(unit)->mClient.latency())};
auto ft = Wrapper::getInterfaceTable();
std::stringstream ss; std::stringstream ss;
ss << '/' << Wrapper::getName() << "_latency"; ss << '/' << Wrapper::getName() << "_latency";
std::cout << ss.str() << '\n'; std::cout << ss.str() << std::endl;
ft->fSendNodeReply(&unit->mParent->mNode, -1, ss.str().c_str(), 1, l); ft->fSendNodeReply(&unit->mParent->mNode, -1, ss.str().c_str(), 1, l);
} }
RealTime() RealTime()
: mControlsIterator{mInBuf + mSpecialIndex + 1,static_cast<size_t>(static_cast<ptrdiff_t>(mNumInputs) - mSpecialIndex - 1)} : mControlsIterator{mInBuf + mSpecialIndex + 1,
, mParams{Wrapper::Client::getParameterDescriptors()} static_cast<index>(mNumInputs) - mSpecialIndex - 1},
, mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)} mParams{Wrapper::Client::getParameterDescriptors()},
mClient{Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld,
mControlsIterator, true)}
{} {}
void init() void init()
{ {
assert(!(mClient.audioChannelsOut() > 0 && mClient.controlChannelsOut() > 0) && assert(
"Client can't have both audio and control outputs"); !(mClient.audioChannelsOut() > 0 && mClient.controlChannelsOut() > 0) &&
"Client can't have both audio and control outputs");
// If we don't the number of arguments we expect, the language side code is
// probably the wrong version set plugin to no-op, squawk, and bail;
if (mControlsIterator.size() != Client::getParameterDescriptors().count())
{
mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs;
std::cout
<< "ERROR: " << Wrapper::getName()
<< " wrong number of arguments. Expected "
<< Client::getParameterDescriptors().count() << ", got "
<< mControlsIterator.size()
<< ". Your .sc file and binary plugin might be different versions."
<< std::endl;
return;
}
mClient.sampleRate(fullSampleRate()); mClient.sampleRate(fullSampleRate());
mInputConnections.reserve(mClient.audioChannelsIn()); mInputConnections.reserve(asUnsigned(mClient.audioChannelsIn()));
mOutputConnections.reserve(mClient.audioChannelsOut()); mOutputConnections.reserve(asUnsigned(mClient.audioChannelsOut()));
mAudioInputs.reserve(mClient.audioChannelsIn()); mAudioInputs.reserve(asUnsigned(mClient.audioChannelsIn()));
mOutputs.reserve(std::max(mClient.audioChannelsOut(), mClient.controlChannelsOut())); mOutputs.reserve(asUnsigned(
std::max(mClient.audioChannelsOut(), mClient.controlChannelsOut())));
for (int i = 0; i < static_cast<int>(mClient.audioChannelsIn()); ++i) for (index i = 0; i < mClient.audioChannelsIn(); ++i)
{ {
mInputConnections.emplace_back(isAudioRateIn(i)); mInputConnections.emplace_back(isAudioRateIn(static_cast<int>(i)));
mAudioInputs.emplace_back(nullptr, 0, 0); mAudioInputs.emplace_back(nullptr, 0, 0);
} }
for (int i = 0; i < static_cast<int>(mClient.audioChannelsOut()); ++i) for (index i = 0; i < mClient.audioChannelsOut(); ++i)
{ {
mOutputConnections.emplace_back(true); mOutputConnections.emplace_back(true);
mOutputs.emplace_back(nullptr, 0, 0); mOutputs.emplace_back(nullptr, 0, 0);
} }
for (int i = 0; i < static_cast<int>(mClient.controlChannelsOut()); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } for (index i = 0; i < mClient.controlChannelsOut(); ++i)
{ mOutputs.emplace_back(nullptr, 0, 0); }
mCalcFunc = make_calc_function<RealTime, &RealTime::next>(); mCalcFunc = make_calc_function<RealTime, &RealTime::next>();
Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1);
@ -138,20 +163,30 @@ public:
void next(int) void next(int)
{ {
mControlsIterator.reset(mInBuf + mSpecialIndex + 1); //mClient.audioChannelsIn()); mControlsIterator.reset(mInBuf + mSpecialIndex + 1); // mClient.audioChannelsIn());
Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator); // forward on inputs N + audio inputs as params Wrapper::setParams(
mParams, mWorld->mVerbosity > 0, mWorld,
mControlsIterator); // forward on inputs N + audio inputs as params
mParams.constrainParameterValues(); mParams.constrainParameterValues();
const Unit *unit = this; const Unit* unit = this;
for (size_t i = 0; i < mClient.audioChannelsIn(); ++i) for (index i = 0; i < mClient.audioChannelsIn(); ++i)
{
if (mInputConnections[asUnsigned(i)])
{ mAudioInputs[asUnsigned(i)].reset(IN(i), 0, fullBufferSize()); }
}
for (index i = 0; i < mClient.audioChannelsOut(); ++i)
{ {
if (mInputConnections[i]) mAudioInputs[i].reset(IN(i), 0, fullBufferSize()); assert(i <= std::numeric_limits<int>::max());
if (mOutputConnections[asUnsigned(i)])
mOutputs[asUnsigned(i)].reset(out(static_cast<int>(i)), 0,
fullBufferSize());
} }
for (size_t i = 0; i < mClient.audioChannelsOut(); ++i) for (index i = 0; i < mClient.controlChannelsOut(); ++i)
{ {
if (mOutputConnections[i]) mOutputs[i].reset(out(static_cast<int>(i)), 0, fullBufferSize()); assert(i <= std::numeric_limits<int>::max());
mOutputs[asUnsigned(i)].reset(out(static_cast<int>(i)), 0, 1);
} }
for (size_t i = 0; i < mClient.controlChannelsOut(); ++i) { mOutputs[i].reset(out(static_cast<int>(i)), 0, 1); } mClient.process(mAudioInputs, mOutputs, mContext);
mClient.process(mAudioInputs, mOutputs,mContext);
} }
private: private:
@ -163,97 +198,107 @@ private:
FluidContext mContext; FluidContext mContext;
protected: protected:
ParamSetType mParams; ParamSetType mParams;
Client mClient; Client mClient;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// Non Real Time Processor /// Non Real Time Processor
/// This is also a UGen, but the main action is delegated off to a worker thread, via the NRT thread. /// This is also a UGen, but the main action is delegated off to a worker
/// The RT bit is there to allow us (a) to poll our thread and (b) emit a kr progress update /// thread, via the NRT thread. The RT bit is there to allow us (a) to poll our
/// thread and (b) emit a kr progress update
template <typename Client, typename Wrapper> template <typename Client, typename Wrapper>
class NonRealTime: public SCUnit class NonRealTime : public SCUnit
{ {
using ParamSetType = typename Client::ParamSetType; using ParamSetType = typename Client::ParamSetType;
public: public:
static void setup(InterfaceTable* ft, const char* name)
static void setup(InterfaceTable *ft, const char *name)
{ {
registerUnit<Wrapper>(ft, name); registerUnit<Wrapper>(ft, name);
ft->fDefineUnitCmd(name, "cancel", doCancel); ft->fDefineUnitCmd(name, "cancel", doCancel);
ft->fDefineUnitCmd(name, "queue_enabled", [](struct Unit* unit, struct sc_msg_iter* args) ft->fDefineUnitCmd(
{ name, "queue_enabled", [](struct Unit* unit, struct sc_msg_iter* args) {
auto w = static_cast<Wrapper *>(unit); auto w = static_cast<Wrapper*>(unit);
w->mQueueEnabled = args->geti(0); w->mQueueEnabled = args->geti(0);
w->mFifoMsg.Set(w->mWorld,[](FifoMsg* f) w->mFifoMsg.Set(
{ w->mWorld,
auto w = static_cast<Wrapper*>(f->mData); [](FifoMsg* f) {
w->mClient.setQueueEnabled(w->mQueueEnabled); auto w = static_cast<Wrapper*>(f->mData);
},nullptr,w); w->mClient.setQueueEnabled(w->mQueueEnabled);
Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg); },
}); nullptr, w);
ft->fDefineUnitCmd(name, "synchronous", [](struct Unit* unit, struct sc_msg_iter* args) Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg);
{ });
auto w = static_cast<Wrapper *>(unit); ft->fDefineUnitCmd(
w->mSynchronous = args->geti(0); name, "synchronous", [](struct Unit* unit, struct sc_msg_iter* args) {
w->mFifoMsg.Set(w->mWorld,[](FifoMsg* f) auto w = static_cast<Wrapper*>(unit);
{ w->mSynchronous = args->geti(0);
auto w = static_cast<Wrapper*>(f->mData); w->mFifoMsg.Set(
w->mClient.setSynchronous(w->mSynchronous); w->mWorld,
},nullptr,w); [](FifoMsg* f) {
Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg); auto w = static_cast<Wrapper*>(f->mData);
}); w->mClient.setSynchronous(w->mSynchronous);
},
nullptr, w);
Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg);
});
} }
/// Penultimate input is the doneAction, final is blocking mode. Neither are params, so we skip them in the controlsIterator /// Penultimate input is the doneAction, final is blocking mode. Neither are
NonRealTime() : /// params, so we skip them in the controlsIterator
mControlsIterator{mInBuf,static_cast<size_t>(static_cast<ptrdiff_t>(mNumInputs) - mSpecialIndex - 2)} NonRealTime()
, mParams{Wrapper::Client::getParameterDescriptors()} : mControlsIterator{mInBuf, index(mNumInputs) - mSpecialIndex - 2},
, mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)} mParams{Wrapper::Client::getParameterDescriptors()},
, mSynchronous{mNumInputs > 2 ? (in0(static_cast<int>(mNumInputs - 1)) > 0) : false} mClient{Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld,
mControlsIterator, true)},
mSynchronous{mNumInputs > 2 ? (in0(int(mNumInputs) - 1) > 0) : false}
{} {}
~NonRealTime() ~NonRealTime()
{ {
if(mClient.state() == ProcessState::kProcessing) if (mClient.state() == ProcessState::kProcessing)
{ {
std::cout << Wrapper::getName() << ": Processing cancelled \n"; std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl;
Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode,1,"/done",0,nullptr); Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode, 1, "/done",
0, nullptr);
} }
//processing will be cancelled in ~NRTThreadAdaptor() // processing will be cancelled in ~NRTThreadAdaptor()
} }
/// No option of not using a worker thread for now /// No option of not using a worker thread for now
/// init() sets up the NRT process via the SC NRT thread, and then sets our UGen calc function going /// init() sets up the NRT process via the SC NRT thread, and then sets our
/// UGen calc function going
void init() void init()
{ {
mFifoMsg.Set(mWorld, initNRTJob, nullptr, this); mFifoMsg.Set(mWorld, initNRTJob, nullptr, this);
mWorld->ft->fSendMsgFromRT(mWorld,mFifoMsg); mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg);
//we want to poll thread roughly every 20ms // we want to poll thread roughly every 20ms
checkThreadInterval = static_cast<size_t>(0.02 / controlDur()); checkThreadInterval = static_cast<index>(0.02 / controlDur());
set_calc_function<NonRealTime, &NonRealTime::poll>(); set_calc_function<NonRealTime, &NonRealTime::poll>();
}; };
/// The calc function. Checks to see if we've cancelled, spits out progress, launches tidy up when complete /// The calc function. Checks to see if we've cancelled, spits out progress,
/// launches tidy up when complete
void poll(int) void poll(int)
{ {
out0(0) = mDone ? 1.0 : static_cast<float>(mClient.progress()); out0(0) = mDone ? 1.0f : static_cast<float>(mClient.progress());
if(0 == pollCounter++ && !mCheckingForDone) if (0 == pollCounter++ && !mCheckingForDone)
{ {
mCheckingForDone = true; mCheckingForDone = true;
mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), this, mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(),
postProcess, exchangeBuffers, tidyUp, destroy, this, postProcess, exchangeBuffers,
0, nullptr); tidyUp, destroy, 0, nullptr);
} }
pollCounter %= checkThreadInterval; pollCounter %= checkThreadInterval;
} }
/// To be called on NRT thread. Validate parameters and commence processing in new thread /// To be called on NRT thread. Validate parameters and commence processing in
/// new thread
static void initNRTJob(FifoMsg* f) static void initNRTJob(FifoMsg* f)
{ {
auto w = static_cast<Wrapper*>(f->mData); auto w = static_cast<Wrapper*>(f->mData);
@ -264,8 +309,9 @@ public:
if (!result.ok()) if (!result.ok())
{ {
std::cout << "ERROR: " << Wrapper::getName() << ": " << result.message().c_str() << std::endl; std::cout << "ERROR: " << Wrapper::getName() << ": "
return; << result.message().c_str() << std::endl;
return;
} }
w->mClient.setSynchronous(w->mSynchronous); w->mClient.setSynchronous(w->mSynchronous);
w->mClient.enqueue(w->mParams); w->mClient.enqueue(w->mParams);
@ -273,27 +319,35 @@ public:
} }
/// Check result and report if bad /// Check result and report if bad
static bool postProcess(World*, void *data) static bool postProcess(World*, void* data)
{ {
auto w = static_cast<Wrapper*>(data); auto w = static_cast<Wrapper*>(data);
Result r; Result r;
ProcessState s = w->mClient.checkProgress(r); ProcessState s = w->mClient.checkProgress(r);
if((s==ProcessState::kDone || s==ProcessState::kDoneStillProcessing) if ((s == ProcessState::kDone || s == ProcessState::kDoneStillProcessing) ||
|| (w->mSynchronous && s==ProcessState::kNoProcess) ) //I think this hinges on the fact that when mSynchrous = true, this call will always be behind process() on the command FIFO, so we can assume that if the state is kNoProcess, it has run (vs never having run) (w->mSynchronous &&
s == ProcessState::kNoProcess)) // I think this hinges on the fact that
// when mSynchrous = true, this call
// will always be behind process() on
// the command FIFO, so we can assume
// that if the state is kNoProcess, it
// has run (vs never having run)
{ {
//Given that cancellation from the language now always happens by freeing the // Given that cancellation from the language now always happens by freeing
//synth, this block isn't reached normally. HOwever, if someone cancels using u_cmd, this is what will fire // the synth, this block isn't reached normally. HOwever, if someone
if(r.status() == Result::Status::kCancelled) // cancels using u_cmd, this is what will fire
if (r.status() == Result::Status::kCancelled)
{ {
std::cout << Wrapper::getName() << ": Processing cancelled \n"; std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl;
w->mCancelled = true; w->mCancelled = true;
return false; return false;
} }
if(!r.ok()) if (!r.ok())
{ {
std::cout << "ERROR: " << Wrapper::getName() << ": " << r.message().c_str() << '\n'; std::cout << "ERROR: " << Wrapper::getName() << ": "
<< r.message().c_str() << std::endl;
return false; return false;
} }
@ -304,87 +358,120 @@ public:
} }
/// swap NRT buffers back to RT-land /// swap NRT buffers back to RT-land
static bool exchangeBuffers(World *world, void *data) { return static_cast<Wrapper *>(data)->exchangeBuffers(world); } static bool exchangeBuffers(World* world, void* data)
{
return static_cast<Wrapper*>(data)->exchangeBuffers(world);
}
/// Tidy up any temporary buffers /// Tidy up any temporary buffers
static bool tidyUp(World *world, void *data) { return static_cast<Wrapper *>(data)->tidyUp(world); } static bool tidyUp(World* world, void* data)
{
return static_cast<Wrapper*>(data)->tidyUp(world);
}
/// Now we're actually properly done, call the UGen's done action (possibly destroying this instance) /// Now we're actually properly done, call the UGen's done action (possibly
/// destroying this instance)
static void destroy(World* world, void* data) static void destroy(World* world, void* data)
{ {
auto w = static_cast<Wrapper*>(data); auto w = static_cast<Wrapper*>(data);
if(w->mDone && w->mNumInputs > 2) //don't check for doneAction if UGen has no ins (there should be 3 minimum -> sig, doneAction, blocking mode) if (w->mDone &&
w->mNumInputs >
2) // don't check for doneAction if UGen has no ins (there should be
// 3 minimum -> sig, doneAction, blocking mode)
{ {
int doneAction = static_cast<int>(w->in0(static_cast<int>(w->mNumInputs - 2))); //doneAction is penultimate input; THIS IS THE LAW int doneAction = static_cast<int>(
world->ft->fDoneAction(doneAction,w); w->in0(int(w->mNumInputs) -
2)); // doneAction is penultimate input; THIS IS THE LAW
world->ft->fDoneAction(doneAction, w);
return; return;
} }
w->mCheckingForDone = false; w->mCheckingForDone = false;
} }
static void doCancel(Unit *unit, sc_msg_iter*) static void doCancel(Unit* unit, sc_msg_iter*)
{ {
static_cast<Wrapper *>(unit)->mClient.cancel(); static_cast<Wrapper*>(unit)->mClient.cancel();
} }
private:
static Result validateParameters(NonRealTime *w) private:
static Result validateParameters(NonRealTime* w)
{ {
auto results = w->mParams.constrainParameterValues(); auto results = w->mParams.constrainParameterValues();
for (auto &r : results) for (auto& r : results)
{ {
if (!r.ok()) return r; if (!r.ok()) return r;
} }
return {}; return {};
} }
bool exchangeBuffers(World *world) //RT thread bool exchangeBuffers(World* world) // RT thread
{ {
mParams.template forEachParamType<BufferT, impl::AssignBuffer>(world); mParams.template forEachParamType<BufferT, AssignBuffer>(world);
//At this point, we can see if we're finished and let the language know (or it can wait for the doneAction, but that takes extra time) // At this point, we can see if we're finished and let the language know (or
//use replyID to convey status (0 = normal completion, 1 = cancelled) // it can wait for the doneAction, but that takes extra time) use replyID to
if(mDone) world->ft->fSendNodeReply(&mParent->mNode,0,"/done",0,nullptr); // convey status (0 = normal completion, 1 = cancelled)
if(mCancelled) world->ft->fSendNodeReply(&mParent->mNode,1,"/done",0,nullptr); if (mDone)
world->ft->fSendNodeReply(&mParent->mNode, 0, "/done", 0, nullptr);
if (mCancelled)
world->ft->fSendNodeReply(&mParent->mNode, 1, "/done", 0, nullptr);
return true; return true;
} }
bool tidyUp(World *) //NRT thread bool tidyUp(World*) // NRT thread
{ {
mParams.template forEachParamType<BufferT, impl::CleanUpBuffer>(); mParams.template forEachParamType<BufferT, impl::CleanUpBuffer>();
return true; return true;
} }
template <size_t N, typename T>
struct AssignBuffer
{
void operator()(const typename BufferT::type& p, World* w)
{
if (auto b = static_cast<SCBufferAdaptor*>(p.get())) b->assignToRT(w);
}
};
template <size_t N, typename T>
struct CleanUpBuffer
{
void operator()(const typename BufferT::type& p)
{
if (auto b = static_cast<SCBufferAdaptor*>(p.get())) b->cleanUp();
}
};
FloatControlsIter mControlsIterator;
FifoMsg mFifoMsg;
char* mCompletionMessage = nullptr;
void* mReplyAddr = nullptr;
const char* mName = nullptr;
index checkThreadInterval;
index pollCounter{0};
FloatControlsIter mControlsIterator;
FifoMsg mFifoMsg;
char* mCompletionMessage = nullptr;
void* mReplyAddr = nullptr;
const char *mName = nullptr;
size_t checkThreadInterval;
size_t pollCounter{0};
protected: protected:
ParamSetType mParams; ParamSetType mParams;
Client mClient; Client mClient;
bool mSynchronous{true}; bool mSynchronous{true};
bool mQueueEnabled{false}; bool mQueueEnabled{false};
bool mCheckingForDone{false}; //only write to this from RT thread kthx bool mCheckingForDone{false}; // only write to this from RT thread kthx
bool mCancelled{false}; bool mCancelled{false};
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// An impossible monstrosty /// An impossible monstrosty
template <typename Client, typename Wrapper> template <typename Client, typename Wrapper>
class NonRealTimeAndRealTime : public RealTime<Client, Wrapper>, public NonRealTime<Client, Wrapper> class NonRealTimeAndRealTime : public RealTime<Client, Wrapper>,
public NonRealTime<Client, Wrapper>
{ {
static void setup(InterfaceTable *ft, const char *name) static void setup(InterfaceTable* ft, const char* name)
{ {
RealTime<Client,Wrapper>::setup(ft, name); RealTime<Client, Wrapper>::setup(ft, name);
NonRealTime<Client,Wrapper>::setup(ft, name); NonRealTime<Client, Wrapper>::setup(ft, name);
} }
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Template Specialisations for NRT/RT // Template Specialisations for NRT/RT
@ -394,26 +481,26 @@ class FluidSCWrapperImpl;
template <typename Client, typename Wrapper> template <typename Client, typename Wrapper>
class FluidSCWrapperImpl<Client, Wrapper, std::true_type, std::false_type> class FluidSCWrapperImpl<Client, Wrapper, std::true_type, std::false_type>
: public NonRealTime<Client, Wrapper> : public NonRealTime<Client, Wrapper>
{ {};
//public:
// FluidSCWrapperImpl(World* w, sc_msg_iter *args): NonRealTime<Client, Wrapper>(w,args){};
};
template <typename Client, typename Wrapper> template <typename Client, typename Wrapper>
class FluidSCWrapperImpl<Client, Wrapper, std::false_type, std::true_type> : public RealTime<Client, Wrapper> class FluidSCWrapperImpl<Client, Wrapper, std::false_type, std::true_type>
: public RealTime<Client, Wrapper>
{}; {};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Make base class(es), full of CRTP mixin goodness // Make base class(es), full of CRTP mixin goodness
template <typename Client> template <typename Client>
using FluidSCWrapperBase = FluidSCWrapperImpl<Client, FluidSCWrapper<Client>, typename Client::isNonRealTime, typename Client::isRealTime>; using FluidSCWrapperBase =
FluidSCWrapperImpl<Client, FluidSCWrapper<Client>, isNonRealTime<Client>,
isRealTime<Client>>;
} // namespace impl } // namespace impl
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
///The main wrapper /// The main wrapper
template <typename C> template <typename C>
class FluidSCWrapper : public impl::FluidSCWrapperBase<C> class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
{ {
@ -434,7 +521,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
static auto fromArgs(World *w, FloatControlsIter& args, std::string, int) static auto fromArgs(World *w, FloatControlsIter& args, std::string, int)
{ {
//first is string size, then chars //first is string size, then chars
int size = static_cast<int>(args.next()); index size = args.next();
char* chunk = static_cast<char*>(FluidSCWrapper::getInterfaceTable()->fRTAlloc(w,size + 1)); char* chunk = static_cast<char*>(FluidSCWrapper::getInterfaceTable()->fRTAlloc(w,size + 1));
if (!chunk) { if (!chunk) {
@ -442,7 +529,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
return std::string{""}; return std::string{""};
} }
for(int i = 0; i < size; ++i) for(index i = 0; i < size; ++i)
chunk[i] = static_cast<char>(args.next()); chunk[i] = static_cast<char>(args.next());
chunk[size] = 0; //terminate string chunk[size] = 0; //terminate string
@ -475,8 +562,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
static auto fromArgs(World *w, ArgType args, InputBufferT::type&, int) static auto fromArgs(World *w, ArgType args, InputBufferT::type&, int)
{ {
typename LongT::type bufnum = static_cast<LongT::type>(fromArgs(w, args, LongT::type(), -1)); typename LongT::type bufnum =
return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); static_cast<LongT::type>(fromArgs(w, args, LongT::type(), -1));
return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w)
: nullptr);
} }
template<typename P> template<typename P>
@ -494,7 +583,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
template <typename ArgType, size_t N, typename T> template <typename ArgType, size_t N, typename T>
struct Setter struct Setter
{ {
static constexpr size_t argSize = C::getParameterDescriptors().template get<N>().fixedSize; static constexpr index argSize = C::getParameterDescriptors().template get<N>().fixedSize;
typename T::type operator()(World *w, ArgType args) typename T::type operator()(World *w, ArgType args)
{ {
@ -506,9 +595,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
} }
ParamLiteralConvertor<T, argSize> a; ParamLiteralConvertor<T, argSize> a;
using LiteralType = typename ParamLiteralConvertor<T, argSize>::LiteralType; using LiteralType =
typename ParamLiteralConvertor<T, argSize>::LiteralType;
for (size_t i = 0; i < argSize; i++) for (index i = 0; i < argSize; i++)
a[i] = static_cast<LiteralType>(ParamReader<ArgType>::fromArgs(w, args, a[0], 0)); a[i] = static_cast<LiteralType>(ParamReader<ArgType>::fromArgs(w, args, a[0], 0));
return a.value(); return a.value();
@ -715,40 +805,57 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
static void doVersion(Unit*, sc_msg_iter*)
{
std::cout << "Fluid Corpus Manipualtion Toolkit version " << fluidVersion()
<< std::endl;
}
public: public:
using Client = C; using Client = C;
using ParameterSetType = typename C::ParamSetType; using ParameterSetType = typename C::ParamSetType;
FluidSCWrapper() FluidSCWrapper() { impl::FluidSCWrapperBase<Client>::init(); }
{
impl::FluidSCWrapperBase<Client>::init();
}
static const char *getName(const char *setName = nullptr) static const char* getName(const char* setName = nullptr)
{ {
static const char *name = nullptr; static const char* name = nullptr;
return (name = setName ? setName : name); return (name = setName ? setName : name);
} }
static InterfaceTable *getInterfaceTable(InterfaceTable *setTable = nullptr) static InterfaceTable* getInterfaceTable(InterfaceTable* setTable = nullptr)
{ {
static InterfaceTable *ft = nullptr; static InterfaceTable* ft = nullptr;
return (ft = setTable ? setTable : ft); return (ft = setTable ? setTable : ft);
} }
static void setup(InterfaceTable *ft, const char *name) static void setup(InterfaceTable* ft, const char* name)
{ {
getName(name); getName(name);
getInterfaceTable(ft); getInterfaceTable(ft);
impl::FluidSCWrapperBase<Client>::setup(ft, name); impl::FluidSCWrapperBase<Client>::setup(ft, name);
Client::getMessageDescriptors().template iterate<SetupMessage>(); Client::getMessageDescriptors().template iterate<SetupMessage>();
ft->fDefineUnitCmd(name, "version", doVersion);
} }
static auto& setParams(ParameterSetType& p, bool verbose, World* world, FloatControlsIter& inputs, bool constrain = false) static auto& setParams(ParameterSetType& p, bool verbose, World* world,
FloatControlsIter& inputs, bool constrain = false)
{ {
p.template setParameterValues<ControlSetter>(verbose, world, inputs); // We won't even try and set params if the arguments don't match
if(inputs.remain() > 0) std::cout << "WARNING: "<< getName() << " received " << inputs.remain() << " more parameters than expected. Perhaps your binary plugins and SC sources are different versions\n"; if (inputs.size() == C::getParameterDescriptors().count())
if (constrain) p.constrainParameterValues(); {
p.template setParameterValues<ControlSetter>(verbose, world, inputs);
if (constrain) p.constrainParameterValues();
}
else
{
std::cout << "ERROR: " << getName()
<< ": parameter count mismatch. Perhaps your binary plugins "
"and SC sources are different versions"
<< std::endl;
}
return p; return p;
} }

@ -1,11 +1,21 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#pragma once #pragma once
#include <SC_PlugIn.h>
#include <SC_Errors.h>
#include <boost/align/aligned_alloc.hpp> #include <boost/align/aligned_alloc.hpp>
#include <cctype>
#include <data/FluidTensor.hpp>
#include <clients/common/BufferAdaptor.hpp> #include <clients/common/BufferAdaptor.hpp>
#include <data/FluidTensor.hpp>
#include <SC_Errors.h>
#include <SC_PlugIn.h>
#include <cctype>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@ -13,29 +23,25 @@
#include <vector> #include <vector>
namespace fluid namespace fluid {
{ namespace client {
namespace client
{
/** /**
A descendent of SndBuf that will populate itself A descendent of SndBuf that will populate itself
from the NRT mirror buffers given a world and a bufnum from the NRT mirror buffers given a world and a bufnum
**/ **/
struct NRTBuf { struct NRTBuf
NRTBuf(SndBuf *b) {
: mBuffer(b) NRTBuf(SndBuf* b) : mBuffer(b) {}
{ NRTBuf(World* world, index bufnum, bool rt = false)
} : NRTBuf(rt ? World_GetBuf(world, static_cast<uint32>(bufnum))
NRTBuf(World *world, uint32 bufnum, bool rt = false) : World_GetNRTBuf(world, static_cast<uint32>(bufnum)))
: NRTBuf(rt ? World_GetBuf(world, bufnum)
: World_GetNRTBuf(world, bufnum))
{ {
if (mBuffer && !static_cast<bool>(mBuffer->samplerate)) if (mBuffer && !static_cast<bool>(mBuffer->samplerate))
mBuffer->samplerate = world->mFullRate.mSampleRate; mBuffer->samplerate = world->mFullRate.mSampleRate;
} }
protected: protected:
SndBuf *mBuffer; SndBuf* mBuffer;
}; };
/** /**
@ -57,30 +63,24 @@ protected:
class SCBufferAdaptor : public NRTBuf, public client::BufferAdaptor class SCBufferAdaptor : public NRTBuf, public client::BufferAdaptor
{ {
public: public:
// SCBufferAdaptor() = delete; // SCBufferAdaptor() = delete;
SCBufferAdaptor(const SCBufferAdaptor &) = delete; SCBufferAdaptor(const SCBufferAdaptor&) = delete;
SCBufferAdaptor& operator=(const SCBufferAdaptor &) = delete; SCBufferAdaptor& operator=(const SCBufferAdaptor&) = delete;
SCBufferAdaptor(SCBufferAdaptor&&) = default; SCBufferAdaptor(SCBufferAdaptor&&) = default;
SCBufferAdaptor& operator=(SCBufferAdaptor&&) = default; SCBufferAdaptor& operator=(SCBufferAdaptor&&) = default;
SCBufferAdaptor(intptr_t bufnum,World *world, bool rt = false) SCBufferAdaptor(index bufnum, World* world, bool rt = false)
: NRTBuf(world, static_cast<uint32>(bufnum), rt) : NRTBuf(world, bufnum, rt), mBufnum(bufnum), mWorld(world)
, mBufnum(bufnum) {}
, mWorld(world)
{
}
SCBufferAdaptor() = default;
~SCBufferAdaptor(){ cleanUp(); } ~SCBufferAdaptor() { cleanUp(); }
void assignToRT(World *rtWorld) void assignToRT(World* rtWorld)
{ {
SndBuf *rtBuf = World_GetBuf(rtWorld, static_cast<uint32>(mBufnum)); SndBuf* rtBuf = World_GetBuf(rtWorld, static_cast<uint32>(mBufnum));
*rtBuf = *mBuffer; *rtBuf = *mBuffer;
rtWorld->mSndBufUpdates[mBufnum].writes++; rtWorld->mSndBufUpdates[mBufnum].writes++;
} }
@ -101,76 +101,74 @@ public:
// knows about // knows about
bool valid() const override bool valid() const override
{ {
return (mBuffer && mBufnum >= 0 && mBufnum < mWorld->mNumSndBufs); return (mBuffer && mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs));
} }
bool exists() const override bool exists() const override { return true; }
{
return true;
}
FluidTensorView<float, 1> samps(size_t channel) override FluidTensorView<float, 1> samps(index channel) override
{ {
FluidTensorView<float, 2> v{mBuffer->data, 0, FluidTensorView<float, 2> v{mBuffer->data, 0, mBuffer->frames,
static_cast<size_t>(mBuffer->frames), mBuffer->channels};
static_cast<size_t>(mBuffer->channels)};
return v.col(channel); return v.col(channel);
} }
FluidTensorView<float, 1> samps(size_t offset, size_t nframes, FluidTensorView<float, 1> samps(index offset, index nframes,
size_t chanoffset) override index chanoffset) override
{ {
FluidTensorView<float, 2> v{mBuffer->data, 0, FluidTensorView<float, 2> v{mBuffer->data, 0, mBuffer->frames,
static_cast<size_t>(mBuffer->frames), mBuffer->channels};
static_cast<size_t>(mBuffer->channels)};
return v(fluid::Slice(offset, nframes), fluid::Slice(chanoffset, 1)).col(0); return v(fluid::Slice(offset, nframes), fluid::Slice(chanoffset, 1)).col(0);
} }
FluidTensorView<const float, 1> samps(size_t channel) const override FluidTensorView<const float, 1> samps(index channel) const override
{ {
FluidTensorView<const float, 2> v{mBuffer->data, 0, FluidTensorView<const float, 2> v{mBuffer->data, 0, mBuffer->frames,
static_cast<size_t>(mBuffer->frames), mBuffer->channels};
static_cast<size_t>(mBuffer->channels)};
return v.col(channel); return v.col(channel);
} }
FluidTensorView<const float, 1> samps(size_t offset, size_t nframes, FluidTensorView<const float, 1> samps(index offset, index nframes,
size_t chanoffset) const override index chanoffset) const override
{ {
FluidTensorView<const float, 2> v{mBuffer->data, 0, FluidTensorView<const float, 2> v{mBuffer->data, 0, mBuffer->frames,
static_cast<size_t>(mBuffer->frames), mBuffer->channels};
static_cast<size_t>(mBuffer->channels)};
return v(fluid::Slice(offset, nframes), fluid::Slice(chanoffset, 1)).col(0); return v(fluid::Slice(offset, nframes), fluid::Slice(chanoffset, 1)).col(0);
} }
size_t numFrames() const override index numFrames() const override
{ {
return valid() ? static_cast<size_t>(this->mBuffer->frames) : 0u; return valid() ? this->mBuffer->frames : 0;
} }
size_t numChans() const override index numChans() const override
{ {
return valid() ? static_cast<size_t>(this->mBuffer->channels) : 0u; return valid() ? this->mBuffer->channels : 0;
} }
double sampleRate() const override { return valid() ? mBuffer->samplerate : 0; } double sampleRate() const override
{
return valid() ? mBuffer->samplerate : 0;
}
std::string asString() const override { return std::to_string(bufnum()); } std::string asString() const override { return std::to_string(bufnum()); }
const Result resize(size_t frames, size_t channels, double sampleRate) override const Result resize(index frames, index channels, double sampleRate) override
{ {
SndBuf *thisThing = mBuffer; SndBuf* thisThing = mBuffer;
mOldData = thisThing->data; mOldData = thisThing->data;
int allocResult = mWorld->ft->fBufAlloc(mBuffer, static_cast<int>(channels), static_cast<int>(frames), sampleRate); int allocResult =
mWorld->ft->fBufAlloc(mBuffer, static_cast<int>(channels),
static_cast<int>(frames), sampleRate);
Result r; Result r;
if(allocResult != kSCErr_None) if (allocResult != kSCErr_None)
{ {
r.set(Result::Status::kError); r.set(Result::Status::kError);
r.addMessage("Resize on buffer ", bufnum(), " failed."); r.addMessage("Resize on buffer ", bufnum(), " failed.");
@ -178,22 +176,20 @@ public:
return r; return r;
} }
intptr_t bufnum() const { return mBufnum; } index bufnum() const { return mBufnum; }
void realTime(bool rt) { mRealTime = rt; } void realTime(bool rt) { mRealTime = rt; }
protected: protected:
bool mRealTime{false};
bool mRealTime{false}; float* mOldData{0};
float *mOldData{0}; index mBufnum;
intptr_t mBufnum; World* mWorld;
World *mWorld;
}; };
std::ostream& operator <<(std::ostream& os, SCBufferAdaptor& b) std::ostream& operator<<(std::ostream& os, SCBufferAdaptor& b)
{ {
return os << b.bufnum(); return os << b.bufnum();
} }
} // namespace client } // namespace client
} // namespace fluid } // namespace fluid

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2017-2019 University of Huddersfield
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,11 @@
FluidAmpGate : UGen {
*ar { arg in = 0, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, maxSize = 88200;
^this.multiNew('audio', in.asAudioRateInput(this), rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize)
}
checkInputs {
if(inputs.at(12).rate != 'scalar') {
^(": maxSize cannot be modulated.");
};
^this.checkValidInputs;
}
}

@ -1,11 +1,9 @@
FluidAmpSlice : UGen { FluidAmpSlice : UGen {
*ar { arg in = 0, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, maxSize = 88200; *ar { arg in = 0, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85;
^this.multiNew('audio', in.asAudioRateInput(this), absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0)
^this.multiNew('audio', in.asAudioRateInput(this), fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq)
} }
checkInputs { checkInputs {
if(inputs.at(16).rate != 'scalar') {
^(": maxSize cannot be modulated.");
};
^this.checkValidInputs; ^this.checkValidInputs;
} }
} }

@ -0,0 +1,37 @@
FluidBufAmpGate : UGen {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, doneAction = 0, blocking|
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};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, doneAction, blocking);
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, doneAction,blocking:0);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, action |
^FluidNRTProcess.new(
server, this, action, [indices]
).process(
source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, action|
^FluidNRTProcess.new(
server, this, action, [indices], blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq
);
}
}

@ -1,7 +1,6 @@
FluidBufAmpSlice : UGen { FluidBufAmpSlice : UGen {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, doneAction = 0, blocking| *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, doneAction = 0, blocking|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
source = source.asUGenInput; source = source.asUGenInput;
indices = indices.asUGenInput; indices = indices.asUGenInput;
@ -9,29 +8,29 @@ FluidBufAmpSlice : UGen {
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0, doneAction, blocking); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, doneAction, blocking);
} }
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, doneAction = 0| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, 0, doneAction,blocking:0); ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, doneAction,blocking:0);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, action | *process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, action |
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [indices] server, this, action, [indices]
).process( ).process(
source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq
); );
} }
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, action| *processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, action|
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [indices], blocking: 1 server, this, action, [indices], blocking: 1
).process( ).process(
source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq
); );
} }
} }

@ -1,5 +1,5 @@
FluidBufMelBands : UGen { FluidBufMelBands : UGen {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -13,26 +13,26 @@ FluidBufMelBands : UGen {
//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) //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 // same for maxNumBands which is passed numBands
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
} }
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, windowSize, hopSize, fftSize, doneAction); ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, doneAction);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action| *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [features] server, this, action, [features]
).process( ).process(
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize
); );
} }
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action| *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [features], blocking:1 server, this, action, [features], blocking:1
).process( ).process(
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize
); );
} }
} }

@ -1,47 +1,47 @@
FluidBufNMF : UGen { FluidBufNMF : UGen {
*new1 {|rate, 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, doneAction = 0, blocking = 0| *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0, blocking = 0|
source = source.asUGenInput; source = source.asUGenInput;
destination = destination.asUGenInput; resynth = resynth.asUGenInput;
bases = bases.asUGenInput; bases = bases.asUGenInput;
activations = activations.asUGenInput; activations = activations.asUGenInput;
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
destination = destination ? -1; resynth = resynth ? -1;
bases = bases ? -1; bases = bases ? -1;
activations = activations ? -1; activations = activations ? -1;
^super.new1(rate,source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction, blocking); ^super.new1(rate,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction, blocking);
} }
*kr {|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, doneAction = 0| *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0|
^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction); ^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action| *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [destination, bases, activations].select{|x| x!= -1} server, this, action, [resynth, bases, activations].select{|x| x!= -1}
).process( ).process(
source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
); );
} }
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action| *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [destination, bases, activations].select{|x| x!= -1},blocking: 1 server, this, action, [resynth, bases, activations].select{|x| x!= -1},blocking: 1
).process( ).process(
source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
); );
} }
} }

@ -1,5 +1,5 @@
FluidBufNoveltySlice : UGen { FluidBufNoveltySlice : UGen {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0 | *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0 |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -9,29 +9,29 @@ FluidBufNoveltySlice : UGen {
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, doneAction, blocking); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, doneAction, blocking);
} }
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0 | *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0 |
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, doneAction); ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, doneAction);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action | *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, action |
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [indices] server, this, action, [indices]
).process( ).process(
source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize
); );
} }
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action | *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, action |
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [indices], blocking:1 server, this, action, [indices], blocking:1
).process( ).process(
source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize
); );
} }
} }

@ -1,6 +1,6 @@
FluidBufSines : UGen{ FluidBufSines : UGen{
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -13,26 +13,26 @@ FluidBufSines : UGen{
//NB For wrapped versions of NRT classes, we set the params for maxima to //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) //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)
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
} }
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, doneAction); ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, doneAction);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action| *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [sines, residual].select{|x| x!= -1} server, this, action, [sines, residual].select{|x| x!= -1}
).process( ).process(
source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize
); );
} }
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action| *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new( ^FluidNRTProcess.new(
server, this, action, [sines, residual].select{|x| x!= -1}, blocking: 1 server, this, action, [sines, residual].select{|x| x!= -1}, blocking: 1
).process( ).process(
source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize
); );
} }

@ -1,7 +1,7 @@
FluidMelBands : MultiOutUGen { FluidMelBands : MultiOutUGen {
*kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 120, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384; *kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 120, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384;
^this.multiNew('control', in.asAudioRateInput(this), numBands, minFreq, maxFreq, maxNumBands, windowSize, hopSize, fftSize, maxFFTSize); ^this.multiNew('control', in.asAudioRateInput(this), numBands, minFreq, maxFreq, maxNumBands, normalize, windowSize, hopSize, fftSize, maxFFTSize);
} }
init {arg ...theInputs; init {arg ...theInputs;

@ -1,6 +1,6 @@
FluidNoveltySlice : UGen { FluidNoveltySlice : UGen {
*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; *ar { arg in = 0, feature = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, minSliceLength = 2, 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) ^this.multiNew('audio', in.asAudioRateInput(this), feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, maxKernelSize, maxFilterSize)
} }
checkInputs { checkInputs {
if(inputs.at(8).rate != 'scalar') { if(inputs.at(8).rate != 'scalar') {

@ -1,6 +1,6 @@
FluidSines : MultiOutUGen { FluidSines : MultiOutUGen {
*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; *ar { arg in = 0, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize=16384;
^this.multiNew('audio', in.asAudioRateInput(this), bandwidth, threshold, minTrackLen, magWeight, freqWeight ,windowSize, hopSize, fftSize, maxFFTSize) ^this.multiNew('audio', in.asAudioRateInput(this), bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize)
} }
init { arg ... theInputs; init { arg ... theInputs;
inputs = theInputs; inputs = theInputs;
@ -11,7 +11,7 @@ FluidSines : MultiOutUGen {
^channels ^channels
} }
checkInputs { checkInputs {
if(inputs.at(9).rate != 'scalar') { if(inputs.at(13).rate != 'scalar') {
^(": maxFFTSize cannot be modulated."); ^(": maxFFTSize cannot be modulated.");
}; };
^this.checkNInputs(1) ^this.checkNInputs(1)

@ -0,0 +1,100 @@
(
var win, soundFileView, freqSscope,loadButton, loopButton;
var harmSlider, percSlider, mixSlider;
var soundFile, buffer;
var synthDef, synth;
var makeSynthDef;
Font.default = Font("Monaco", 16);
buffer = Buffer.new;
win = Window.new("HPSS", Rect(200,200,800,450)).background_(Color.gray);
soundFileView = SoundFileView.new(win)
.gridOn_(false)
.waveColors_([Color.white]);
loadButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_([["Load", Color.grey, Color.grey(0.8)]]);
loopButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_(
[["Play", Color.grey, Color.grey(0.8)],
["Stop", Color.grey, Color.grey(0.2)]]
);
harmSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
percSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
mixSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
freqSscope = FreqScopeView(win, server:Server.default);
freqSscope.active_(true);
loadButton.action_{
FileDialog({ |path|
soundFile = SoundFile.new;
soundFile.openRead(path[0]);
buffer = Buffer.read(Server.default, path[0]);
soundFileView.soundfile = soundFile;
soundFileView.read(0, soundFile.numFrames);
});
};
loopButton.action_{|but|
if(but.value == 1, {
synth = Synth(\hpssExtractionDemo, [\buffer, buffer.bufnum]);
mixSlider.action.value(mixSlider);
},{
synth.free;
});
};
mixSlider.action_{|slider|
synth.set(\bal, ControlSpec(0, 1).map(slider.value));
};
makeSynthDef = {
synthDef = SynthDef(\hpssExtractionDemo,
{|buffer, bal = 0.5|
var player, fhpss, mix;
var harmSize = (2 * ControlSpec(1, 100, step:1).map(harmSlider.value)) - 1;
var percSize = (2 * ControlSpec(1,100, step:1).map(percSlider.value)) - 1;
player = PlayBuf.ar(1, buffer, loop:1);
fhpss = FluidHPSS.ar(in: player, harmFilterSize: harmSize, percFilterSize: percSize, maskingMode: 1, harmThreshFreq1: 0.1, harmThreshAmp1: 0, harmThreshFreq2: 0.5, harmThreshAmp2: 0, percThreshFreq1: 0.1, percThreshAmp1: 0, percThreshFreq2: 0.5, percThreshAmp2: 0, windowSize: 1024, hopSize: 256, fftSize: -1);
mix =(bal * fhpss[0]) + ((1 - bal) * fhpss[1]);
Out.ar(0,Pan2.ar(mix));
}
).add;
};
win.layout_(
VLayout(
[
HLayout(
[loadButton, stretch:1],
[soundFileView, stretch:5]
), stretch:2
],
[
HLayout(
[loopButton, stretch:1],
[VLayout(
HLayout(StaticText(win).string_("H Size ").minWidth_(100), harmSlider),
HLayout(StaticText(win).string_("P Size").minWidth_(100), percSlider),
HLayout(StaticText(win).string_("Mix").minWidth_(100), mixSlider)
), stretch:5]
), stretch:2
],
[freqSscope, stretch:2]
)
);
makeSynthDef.value;
win.front;
)

@ -0,0 +1,111 @@
(
var server;
var win, soundFileView, loadButton, loopButton;
var sliders;
var soundFile, audioBuffer, destBuffer;
var synthDef, synth;
var sl1, sl2, sl3, sl4;
server = Server.default;
Font.default = Font("Monaco", 16);
audioBuffer = Buffer.new;
destBuffer = Buffer.new;
synthDef = SynthDef(\nmfDemo,{|bufnum, a1, a2, a3, a4|
var p = PlayBuf.ar(4, bufnum, loop:1);
var mix = (a1*p[0]) + (a2 * p[1]) + (a3*p[2]) + (a4*p[3]);
Out.ar(0, Pan2.ar(mix));
}).add;
win = Window.new("NMF4",
Rect(200,200,800,450)).background_(Color.gray);
soundFileView = SoundFileView.new(win)
.gridOn_(false)
.waveColors_([Color.white]);
loadButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_([["Load", Color.grey, Color.grey(0.8)],
["Wait", Color.grey, Color.grey(0.2)]]
);
loopButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_(
[["Play", Color.grey, Color.grey(0.8)],
["Stop", Color.grey, Color.grey(0.2)]]
);
sliders = Array.fill(4, {|i|
var s = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
s.action_{
var sym = ("a"++(i+1)).asSymbol;
synth.set(sym, ControlSpec(0, 1).map(s.value));
}
});
loadButton.action_{
FileDialog({ |path|
soundFile = SoundFile.new;
soundFile.openRead(path[0]);
soundFileView.soundfile = soundFile;
soundFileView.read(0, soundFile.numFrames);
Routine{
audioBuffer = Buffer.read(server, path[0]);
server.sync;
FluidBufNMF.process(server,
audioBuffer.bufnum,resynth:destBuffer.bufnum, components:4
);
server.sync;
destBuffer.query;
server.sync;
{loadButton.value_(0)}.defer;
}.play;
});
};
loopButton.action_{|but|
var a1 = ControlSpec(0, 1).map(sliders[0].value);
var a2 = ControlSpec(0, 1).map(sliders[1].value);
var a3 = ControlSpec(0, 1).map(sliders[2].value);
var a4 = ControlSpec(0, 1).map(sliders[3].value);
if(but.value == 1, {
synth = Synth(\nmfDemo,
[\bufnum, destBuffer.bufnum, \a1, a1, \a2, a2, \a3, a3, \a4, a4]);
},{
synth.free;
});
};
win.layout_(
VLayout(
[
HLayout(
[loadButton, stretch:1],
[soundFileView, stretch:5]
), stretch:2
],
[
HLayout(
[loopButton, stretch:1],
[VLayout(
HLayout(StaticText(win).string_("source 1 ").minWidth_(100), sliders[0]),
HLayout(StaticText(win).string_("source 2 ").minWidth_(100), sliders[1]),
HLayout(StaticText(win).string_("source 3 ").minWidth_(100), sliders[2]),
HLayout(StaticText(win).string_("source 4 ").minWidth_(100), sliders[3])
), stretch:5]
), stretch:2
]
)
);
win.front;
)

@ -0,0 +1,150 @@
(
var server;
var win, soundFileView,loadButton, processButton;
var ksSlider, thSlider;
var soundFile, audioBuffer, slicesBuffer, slicesArray;
var addSelections, playFunc, stopFunc;
var synthDef, synth;
var synths;
var playing, currentSelection, colors, prevColor;
var qwerty = "1234567890qwertyuiopasdfghjklzxcvbnm";
playing = Array.fill(qwerty.size, {false});
server = Server.default;
Font.default = Font("Monaco", 16);
audioBuffer = Buffer.new;
slicesBuffer = Buffer.new;
colors = Array.fill(qwerty.size, {Color.rand});
synths = Array.fill(qwerty.size, {nil});
synthDef = SynthDef(\noveltySegDemo,{|buf, start, end|
Out.ar(0, BufRd.ar(1, buf, Phasor.ar(1, 1, start, end)));
}).add;
playFunc = {|index|
var dur;
currentSelection = index;
if(playing[index].not){
synths[index] = Synth(\noveltySegDemo,
[\buf, audioBuffer.bufnum,
\start, slicesArray[index],
\end, slicesArray[index+1]
]);
playing[index] = true;
};
soundFileView.setSelectionColor(currentSelection, Color.white);
};
stopFunc = {|index| synths[index].free; playing[index] = false;
soundFileView.setSelectionColor(
index, colors[index]
);
};
win = Window.new("NoveltySegmentation",
Rect(200,200,800,450)).background_(Color.gray);
win.view.keyDownAction_{|view, char, modifiers, unicode, keycode, key|
var num = qwerty.indexOf(char);
if (num.notNil&& slicesArray.notNil){
playFunc.value(num);
}
};
win.view.keyUpAction_{|view, char|
var num = qwerty.indexOf(char);
if(num.notNil){
stopFunc.value(num);
}
};
soundFileView = SoundFileView.new(win)
.gridOn_(false)
.waveColors_([Color.white]);
loadButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_([["Load", Color.grey, Color.grey(0.8)]]);
processButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_(
[["Process", Color.grey, Color.grey(0.8)],
["Wait", Color.grey, Color.grey(0.2)]]
);
ksSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
thSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
loadButton.action_{
FileDialog({ |path|
soundFile = SoundFile.new;
soundFile.openRead(path[0]);
audioBuffer = Buffer.read(server, path[0]);
soundFileView.soundfile = soundFile;
soundFileView.read(0, soundFile.numFrames);
});
};
processButton.action_{|but|
var ks = 2*(ControlSpec(2, 100, step:1).map(ksSlider.value)) - 1;
var th = ControlSpec(0, 1).map(thSlider.value);
if(but.value == 1, {
Routine{
FluidBufNoveltySlice.process(
server,
source:audioBuffer.bufnum,
indices:slicesBuffer.bufnum,
kernelSize:ks,
threshold: th
);
server.sync;
slicesBuffer.loadToFloatArray(action:{|arr|
slicesArray = arr;
{ processButton.value_(0);
addSelections.value(slicesArray)
}.defer;
});
}.play;
});
};
addSelections = {|array|
var nSegments = min(array.size, soundFileView.selections.size) - 1;
soundFileView.selections.do({|sel, i| soundFileView.selectNone(i)});
nSegments.do({|i|
soundFileView.setSelectionStart(i, array[i]);
soundFileView.setSelectionSize(i, array[i+1] - array[i]);
soundFileView.setSelectionColor(i, colors[i]);
});
};
win.layout_(
VLayout(
[
HLayout(
[loadButton, stretch:1],
[soundFileView, stretch:5]
), stretch:2
],
[
HLayout(
[processButton, stretch:1],
[VLayout(
HLayout(StaticText(win).string_("Kernel ").minWidth_(100), ksSlider),
HLayout(StaticText(win).string_(" Threshold").minWidth_(100), thSlider)
), stretch:5]
), stretch:2
]
)
);
win.front;
)

@ -0,0 +1,107 @@
(
var win, soundFileView, freqSscope,loadButton, loopButton;
var thresholdSlider, lenSlider, mixSlider;
var soundFile, buffer;
var synthDef, synth;
Font.default = Font("Monaco", 16);
buffer = Buffer.new;
win = Window.new("SineExtraction",
Rect(200,200,800,450)).background_(Color.gray);
soundFileView = SoundFileView.new(win)
.gridOn_(false)
.waveColors_([Color.white]);
loadButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_([["Load", Color.grey, Color.grey(0.8)]]);
loopButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_(
[["Play", Color.grey, Color.grey(0.8)],
["Stop", Color.grey, Color.grey(0.2)]]
);
thresholdSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
lenSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
mixSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
freqSscope = FreqScopeView(win, server:Server.default);
freqSscope.active_(true);
loadButton.action_{
FileDialog({ |path|
soundFile = SoundFile.new;
soundFile.openRead(path[0]);
buffer = Buffer.read(Server.default, path[0]);
soundFileView.soundfile = soundFile;
soundFileView.read(0, soundFile.numFrames);
});
};
loopButton.action_{|but|
if(but.value == 1, {
synth = Synth(\sineExtractionDemo, [\buffer, buffer.bufnum]);
mixSlider.action.value(mixSlider);
thresholdSlider.action.value(thresholdSlider);
lenSlider.action.value(lenSlider);
},{
synth.free;
});
};
mixSlider.action_{|slider|
synth.set(\bal, ControlSpec(0, 1).map(slider.value));
};
thresholdSlider.action_{|slider|
synth.set(\threshold, ControlSpec(-144, 0).map(slider.value));
};
lenSlider.action_{|slider|
synth.set(\minLength, ControlSpec(0, 30).map(slider.value));
};
synthDef = SynthDef(\sineExtractionDemo,
{|buffer, threshold = 0.9, minLength = 15, bal = 0.5|
var player, fse, mix;
player = PlayBuf.ar(1, buffer, loop:1);
fse = FluidSines.ar(in: player, bandwidth: 76,
detectionThreshold: threshold, minTrackLen: minLength,
windowSize: 2048,
hopSize: 512, fftSize: 8192
);
mix =(bal * fse[0]) + ((1 - bal) * fse[1]);
Out.ar(0,Pan2.ar(mix));
}
).add;
win.layout_(
VLayout(
[
HLayout(
[loadButton, stretch:1],
[soundFileView, stretch:5]
), stretch:2
],
[
HLayout(
[loopButton, stretch:1],
[VLayout(
HLayout(StaticText(win).string_("Threshold ").minWidth_(100), thresholdSlider),
HLayout(StaticText(win).string_("Min Length").minWidth_(100), lenSlider),
HLayout(StaticText(win).string_("Mix").minWidth_(100), mixSlider)
), stretch:5]
), stretch:2
],
[freqSscope, stretch:2]
)
);
win.front;
)

@ -0,0 +1,103 @@
(
var win, soundFileView, freqSscope,loadButton, loopButton;
var fwSlider, bwSlider, mixSlider;
var soundFile, buffer;
var synthDef, synth;
Font.default = Font("Monaco", 16);
buffer = Buffer.new;
win = Window.new("TransientExtraction",
Rect(200,200,800,450)).background_(Color.gray);
soundFileView = SoundFileView.new(win)
.gridOn_(false)
.waveColors_([Color.white]);
loadButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_([["Load", Color.grey, Color.grey(0.8)]]);
loopButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_(
[["Play", Color.grey, Color.grey(0.8)],
["Stop", Color.grey, Color.grey(0.2)]]
);
fwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
bwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
mixSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
freqSscope = FreqScopeView(win, server:Server.default);
freqSscope.active_(true);
loadButton.action_{
FileDialog({ |path|
soundFile = SoundFile.new;
soundFile.openRead(path[0]);
buffer = Buffer.read(Server.default, path[0]);
soundFileView.soundfile = soundFile;
soundFileView.read(0, soundFile.numFrames);
});
};
loopButton.action_{|but|
if(but.value == 1, {
synth = Synth(\transientExtractionDemo, [\buffer, buffer.bufnum]);
mixSlider.action.value(mixSlider);
fwSlider.action.value(fwSlider);
bwSlider.action.value(bwSlider);
},{
synth.free;
});
};
mixSlider.action_{|slider|
synth.set(\bal, ControlSpec(0, 1).map(slider.value));
};
fwSlider.action_{|slider|
synth.set(\fw, ControlSpec(0.0001, 3, \exp).map(slider.value));
};
bwSlider.action_{|slider|
synth.set(\bw, ControlSpec(0.0001, 3, \exp).map(slider.value));
};
synthDef = SynthDef(\transientExtractionDemo,
{|buffer, fw = 3, bw = 1, bal = 0.5|
var player, fte, mix;
player = PlayBuf.ar(1, buffer, loop:1);
fte = FluidTransients.ar(in: player, threshFwd:fw, threshBack:bw, clumpLength:256);
mix =(bal * fte[0]) + ((1 - bal) * fte[1]);
Out.ar(0,Pan2.ar(mix));
}
).add;
win.layout_(
VLayout(
[
HLayout(
[loadButton, stretch:1],
[soundFileView, stretch:5]
), stretch:2
],
[
HLayout(
[loopButton, stretch:1],
[VLayout(
HLayout(StaticText(win).string_("Forward Th ").minWidth_(100), fwSlider),
HLayout(StaticText(win).string_("Backward Th").minWidth_(100), bwSlider),
HLayout(StaticText(win).string_("Mix").minWidth_(100), mixSlider)
), stretch:5]
), stretch:2
],
[freqSscope, stretch:2]
)
);
win.front;
)

@ -0,0 +1,148 @@
(
var server;
var win, soundFileView,loadButton, processButton;
var fwSlider, bwSlider, debounceSlider;
var soundFile, audioBuffer, slicesBuffer, slicesArray;
var addSelections, playFunc, stopFunc;
var synthDef, synth;
var playing, currentSelection, colors, prevColor;
var qwerty = "1234567890qwertyuiopasdfghjklzxcvbnm";
playing = false;
server = Server.default;
Font.default = Font("Monaco", 16);
audioBuffer = Buffer.new;
slicesBuffer = Buffer.new;
colors = Array.fill(64, {Color.rand});
synthDef = SynthDef(\transientSegDemo,{|buf, start, end|
Out.ar(0, BufRd.ar(1, buf, Phasor.ar(1, 1, start, end)));
}).add;
playFunc = {|index|
var dur;
currentSelection = index;
if(playing.not){
synth = Synth(\transientSegDemo,
[\buf, audioBuffer.bufnum,
\start, slicesArray[index],
\end, slicesArray[index+1]
]);
playing = true;
};
soundFileView.setSelectionColor(currentSelection, Color.white);
};
stopFunc = {synth.free; playing = false;
soundFileView.setSelectionColor(currentSelection, colors[currentSelection]);
};
win = Window.new("TransientSegmentation",
Rect(200,200,800,450)).background_(Color.gray);
win.view.keyDownAction_{|view, char, modifiers, unicode, keycode, key|
var num = qwerty.indexOf(char);
if(num.notNil && slicesArray.notNil){
playFunc.value(num);
}
};
win.view.keyUpAction_{stopFunc.value;};
soundFileView = SoundFileView.new(win)
.gridOn_(false)
.waveColors_([Color.white]);
loadButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_([["Load", Color.grey, Color.grey(0.8)]]);
processButton = Button(win, Rect(0, 0, 100, 100))
.minHeight_(150)
.states_(
[["Process", Color.grey, Color.grey(0.8)],
["Wait", Color.grey, Color.grey(0.2)]]
);
fwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
bwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
debounceSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
loadButton.action_{
FileDialog({ |path|
soundFile = SoundFile.new;
soundFile.openRead(path[0]);
audioBuffer = Buffer.read(server, path[0]);
soundFileView.soundfile = soundFile;
soundFileView.read(0, soundFile.numFrames);
});
};
processButton.action_{|but|
var fw = ControlSpec(0.0001, 3, \exp).map(fwSlider.value);
var bw = ControlSpec(0.0001, 3, \exp).map(bwSlider.value);
var db = ControlSpec(1, 4410).map(debounceSlider.value);
if(but.value == 1, {
Routine{
FluidBufTransientSlice.process(
server,
source:audioBuffer.bufnum,
indices:slicesBuffer.bufnum,
threshFwd: fw,
threshBack: bw,
clumpLength:db
);
server.sync;
slicesBuffer.loadToFloatArray(action:{|arr|
slicesArray = arr;
{ processButton.value_(0);
addSelections.value(slicesArray)
}.defer;
});
}.play;
});
};
addSelections = {|array|
var nSegments = min(array.size, soundFileView.selections.size) - 1;
soundFileView.selections.do({|sel, i| soundFileView.selectNone(i)});
nSegments.do({|i|
soundFileView.setSelectionStart(i, array[i]);
soundFileView.setSelectionSize(i, array[i+1] - array[i]);
soundFileView.setSelectionColor(i, colors[i]);
});
};
win.layout_(
VLayout(
[
HLayout(
[loadButton, stretch:1],
[soundFileView, stretch:5]
), stretch:2
],
[
HLayout(
[processButton, stretch:1],
[VLayout(
HLayout(StaticText(win).string_("Forward Th ").minWidth_(100), fwSlider),
HLayout(StaticText(win).string_("Backward Th").minWidth_(100), bwSlider),
HLayout(StaticText(win).string_("Debounce").minWidth_(100), debounceSlider)
), stretch:5]
), stretch:2
]
)
);
win.front;
)

@ -0,0 +1,67 @@
// A complex example of using composition as an Mid-Side FIR filtering process
// load a stereo buffer and initialise the many destinations
(
b = Buffer.read(s,File.realpath(FluidBufCompose.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav");
c = Buffer.new(s);
d = Buffer.new(s);
e = Buffer.new(s);
f = Buffer.new(s);
)
// encode the mid (in c) and the side (in d)
(
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, destination: c);
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, destination: d);
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, startChan: 1, destination: c, destGain: 1.0);
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp * -1.0, startChan: 1, destination: d, destGain: 1.0);
)
// (optional) compare auraly the stereo with the MS
b.play;
{PlayBuf.ar(1,[c,d])}.play;
// The geeky bit: copy the side (buffer d) on itself with specific amplitudes and delays, in effect applying a FIR filter through expensive convolution
// Important: do either of the 3 options below
// option 1: apply a high pass on the side, with a cutoff of nyquist / 4
e.free; e = Buffer.new(s);
(
[1.0, -1.0].do({ arg x,y;
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
});
)
// option 2: apply a high pass on the side, with a cutoff of nyquist / 10
e.free; e = Buffer.new(s);
(
[0.8, -0.32, -0.24, -0.16, -0.08].do({ arg x,y;
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
});
)
// option 3: apply a high pass on the side, with a cutoff of nyquist / 100
e.free; e = Buffer.new(s);
(
[0.982494, -0.066859, -0.064358, -0.061897, -0.059477, -0.057098, -0.054761, -0.052466, -0.050215, -0.048007, -0.045843, -0.043724, -0.041649, -0.03962, -0.037636, -0.035697, -0.033805, -0.031959, -0.030159, -0.028406, -0.026699, -0.025038, -0.023425, -0.021857, -0.020337].do({ arg x,y;
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
});
)
// play the high-passed side buffer
e.play;
// if you want to try the other filters, do not forget to clear the destination buffer since it will add programmatically onto itself and would not create the expected frequency response
// decode the MS back to stereo
(
FluidBufCompose.process(s,c, numChans: 2, gain: -3.0.dbamp, destination: f);
FluidBufCompose.process(s,e, gain: -3.0.dbamp, destination: f, destGain: 1.0);
FluidBufCompose.process(s,e, gain: -3.0.dbamp * -1.0, destination: f, destStartChan: 1, destGain: 1.0);
)
// play the MS processed version
f.play;
// compare with the original
b.play;

@ -0,0 +1,100 @@
// (re)set the source buffers
(
~low = Buffer.sendCollection(s, (Signal.sineFill(4410, Array.fill(3,0) ++ 1)));
~mid = Buffer.sendCollection(s, (Signal.sineFill(4410, Array.fill(12,0) ++ 1)));
~high = Buffer.sendCollection(s, (Signal.sineFill(4410, Array.fill(48,0) ++ 1)));
~piano = Buffer.read(s,File.realpath(FluidBufCompose.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav",0,8820);
)
// draw the buffers to see what happened
(
~low.plot;
~mid.plot;
~high.plot;
~piano.plot;
)
// define the concatenation macro
(
~concat = {
arg x;
if(x.class != Array,
{
"Error - Needs an array as argument".postln;
}, {
Routine{
for (1,x.size - 1, {
arg i;
FluidBufCompose.process(s,x[i],destination:x[0], destStartFrame:x[0].numFrames);
});
"Done!".postln;
}.play;
}
);
}
)
// test various combinations of concatenation
~concat.value([~low,~mid])
~concat.value([~mid,~low,~high])
~concat.value([~mid,~piano,~low])
~concat.value([~mid,~piano])
// check the buffers for the results
////////////////////////////////
// define the merging macro
(
~merge = {
arg x;
if(x.class != Array,
{
"Error - Needs an array as argument".postln;
}, {
Routine{
for (1,x.size - 1, {
arg i;
FluidBufCompose.process(s,x[i],destination:x[0],destGain:1);
});
"Done!".postln;
}.play;
}
);
}
)
// test various combinations of merging
~merge.value([~low,~mid])
~merge.value([~mid,~low,~high])
~merge.value([~mid,~piano,~low])
~merge.value([~mid,~piano])
// check the buffers for the results
////////////////////////////////
// define the stacking macro
(
~stack = {
arg x;
if(x.class != Array,
{
"Error - Needs an array as argument".postln;
}, {
Routine{
for (1,x.size - 1, {
arg i;
FluidBufCompose.process(s,x[i],destination:x[0], destStartChan:x[0].numChannels);
});
"Done!".postln;
}.play;
}
);
}
)
// test various combinations of stacking
~stack.value([~low,~mid])
~stack.value([~mid,~low,~high])
~stack.value([~mid,~piano,~low])
~stack.value([~mid,~piano])
// check the buffers for the results

@ -0,0 +1,202 @@
// using nmf in 'real-time' as a classifier
// how it works: a circular buffer is recording and attacks trigger the process
// if in learning mode, it does a one component nmf which makes an approximation of the base. 3 of those will be copied in 3 different positions of our final 3-component base
// in in guessing mode, it does a thres component nmf from the trained bases and yields the 3 activation peaks, on which it thresholds resynth
//how to use:
// 1. start the server
// 2. select between parenthesis below and execute. You should get a window with 3 pads (bd sn hh) and various menus
// 3. train the 3 classes:
// 3.1 select the learn option
// 3.2 select which class you want to train
// 3.3 play the sound you want to associate with that class a few times (the left audio channel is the source)
// 3.4 click the transfer button
// 3.5 repeat (3.2-3.4) for the other 2 classes.
// 3.x you can observe the 3 bases here:
~classify_bases.plot(numChannels:3)
// 4. classify
// 4.1 select the classify option
// 4.2 press a pad and look at the activation
// 4.3 tweak the thresholds and enjoy the resynthesis. (the right audio channel is the detected class where classA is a bd sound)
// 4.x you can observe the 3 activations here:
~activations.plot(numChannels:3)
/// code to execute first
(
var circle_buf = Buffer.alloc(s,s.sampleRate * 2); // b
var input_bus = Bus.audio(s,1); // g
var classifying = 0; // c
var cur_training_class = 0; // d
var train_base = Buffer.alloc(s, 65); // e
var activation_vals = [0.0,0.0,0.0]; // j
var thresholds = [0.5,0.5,0.5]; // k
var activations_disps;
var analysis_synth;
var osc_func;
var update_rout;
~classify_bases = Buffer.alloc(s, 65, 3); // f
~activations = Buffer.new(s);
// the circular buffer with triggered actions sending the location of the head at the attack
Routine {
SynthDef(\JITcircular,{arg bufnum = 0, input = 0, env = 0;
var head, head2, duration, audioin, halfdur, trig;
duration = BufFrames.kr(bufnum) / 2;
halfdur = duration / 2;
head = Phasor.ar(0,1,0,duration);
head2 = (head + halfdur) % duration;
// circular buffer writer
audioin = In.ar(input,1);
BufWr.ar(audioin,bufnum,head,0);
BufWr.ar(audioin,bufnum,head+duration,0);
trig = FluidAmpSlice.ar(audioin, 10, 1666, 2205, 2205, 12, 9, -47,4410, 85);
// cue the calculations via the language
SendReply.ar(trig, '/attack',head);
Out.ar(0,audioin);
}).add;
// drum sounds taken from original code by snappizz
// https://sccode.org/1-523
// produced further and humanised by PA
SynthDef(\fluidbd, {
|out = 0|
var body, bodyFreq, bodyAmp;
var pop, popFreq, popAmp;
var click, clickAmp;
var snd;
// body starts midrange, quickly drops down to low freqs, and trails off
bodyFreq = EnvGen.ar(Env([Rand(200,300), 120, Rand(45,49)], [0.035, Rand(0.07,0.1)], curve: \exp));
bodyAmp = EnvGen.ar(Env([0,Rand(0.8,1.3),1,0],[0.005,Rand(0.08,0.085),Rand(0.25,0.35)]), doneAction: 2);
body = SinOsc.ar(bodyFreq) * bodyAmp;
// pop sweeps over the midrange
popFreq = XLine.kr(Rand(700,800), Rand(250,270), Rand(0.018,0.02));
popAmp = EnvGen.ar(Env([0,Rand(0.8,1.3),1,0],[0.001,Rand(0.018,0.02),Rand(0.0008,0.0013)]));
pop = SinOsc.ar(popFreq) * popAmp;
// click is spectrally rich, covering the high-freq range
// you can use Formant, FM, noise, whatever
clickAmp = EnvGen.ar(Env.perc(0.001,Rand(0.008,0.012),Rand(0.07,0.12),-5));
click = RLPF.ar(VarSaw.ar(Rand(900,920),0,0.1), 4760, 0.50150150150) * clickAmp;
snd = body + pop + click;
snd = snd.tanh;
Out.ar(out, snd);
}).add;
SynthDef(\fluidsn, {
|out = 0|
var pop, popAmp, popFreq;
var noise, noiseAmp;
var click;
var snd;
// pop makes a click coming from very high frequencies
// slowing down a little and stopping in mid-to-low
popFreq = EnvGen.ar(Env([Rand(3210,3310), 410, Rand(150,170)], [0.005, Rand(0.008,0.012)], curve: \exp));
popAmp = EnvGen.ar(Env.perc(0.001, Rand(0.1,0.12), Rand(0.7,0.9),-5));
pop = SinOsc.ar(popFreq) * popAmp;
// bandpass-filtered white noise
noiseAmp = EnvGen.ar(Env.perc(0.001, Rand(0.13,0.15), Rand(1.2,1.5),-5), doneAction: 2);
noise = BPF.ar(WhiteNoise.ar, 810, 1.6) * noiseAmp;
click = Impulse.ar(0);
snd = (pop + click + noise) * 1.4;
Out.ar(out, snd);
}).add;
SynthDef(\fluidhh, {
|out = 0|
var click, clickAmp;
var noise, noiseAmp, noiseFreq;
// noise -> resonance -> expodec envelope
noiseAmp = EnvGen.ar(Env.perc(0.001, Rand(0.28,0.3), Rand(0.4,0.6), [-20,-15]), doneAction: 2);
noiseFreq = Rand(3900,4100);
noise = Mix(BPF.ar(ClipNoise.ar, [noiseFreq, noiseFreq+141], [0.12, 0.31], [2.0, 1.2])) * noiseAmp;
Out.ar(out, noise);
}).add;
// makes sure all the synthdefs are on the server
s.sync;
// instantiate the JIT-circular-buffer
analysis_synth = Synth(\JITcircular,[\bufnum, circle_buf, \input, input_bus]);
train_base.fill(0,65,0.1);
// instantiate the listener to cue the processing from the language side
osc_func = OSCFunc({ arg msg;
var head_pos = msg[3];
// when an attack happens
if (classifying == 0, {
// if in training mode, makes a single component nmf
FluidBufNMF.process(s, circle_buf, head_pos, 128, bases:train_base, basesMode: 1, windowSize: 128);
}, {
// if in classifying mode, makes a 3 component nmf from the pretrained bases and compares the activations with the set thresholds
FluidBufNMF.process(s, circle_buf, head_pos, 128, components:3, bases:~classify_bases, basesMode: 2, activations:~activations, windowSize: 128, action:{
// we are retrieving and comparing against the 2nd activation, because FFT processes are zero-padded on each sides, therefore the complete 128 samples are in the middle of the analysis.
~activations.getn(3,3,{|x|
activation_vals = x;
if (activation_vals[0] >= thresholds[0], {Synth(\fluidbd,[\out,1])});
if (activation_vals[1] >= thresholds[1], {Synth(\fluidsn,[\out,1])});
if (activation_vals[2] >= thresholds[2], {Synth(\fluidhh,[\out,1])});
defer{
activations_disps[0].string_("A:" ++ activation_vals[0].round(0.01));
activations_disps[1].string_("B:" ++ activation_vals[1].round(0.01));
activations_disps[2].string_("C:" ++ activation_vals[2].round(0.01));
};
});
};
);
});
}, '/attack', s.addr);
// make sure all the synths are instantiated
s.sync;
// GUI for control
{
var win = Window("Control", Rect(100,100,610,100)).front;
Button(win, Rect(10,10,80, 80)).states_([["bd",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidbd, [\out, input_bus], analysis_synth, \addBefore)});
Button(win, Rect(100,10,80, 80)).states_([["sn",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidsn, [\out, input_bus], analysis_synth, \addBefore)});
Button(win, Rect(190,10,80, 80)).states_([["hh",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidhh, [\out, input_bus], analysis_synth,\addBefore)});
StaticText(win, Rect(280,7,85,25)).string_("Select").align_(\center);
PopUpMenu(win, Rect(280,32,85,25)).items_(["learn","classify"]).action_({|value|
classifying = value.value;
if(classifying == 0, {
train_base.fill(0,65,0.1)
});
});
PopUpMenu(win, Rect(280,65,85,25)).items_(["classA","classB","classC"]).action_({|value|
cur_training_class = value.value;
train_base.fill(0,65,0.1);
});
Button(win, Rect(375,65,85,25)).states_([["transfer",Color.black,Color.white]]).mouseDownAction_({
if(classifying == 0, {
// if training
FluidBufCompose.process(s, train_base, numChans:1, destination:~classify_bases, destStartChan:cur_training_class);
});
});
StaticText(win, Rect(470,7,75,25)).string_("Acts");
activations_disps = Array.fill(3, {arg i;
StaticText(win, Rect(470,((i+1) * 20 )+ 7,80,25));
});
StaticText(win, Rect(540,7,55,25)).string_("Thresh").align_(\center);
3.do {arg i;
TextField(win, Rect(540,((i+1) * 20 )+ 7,55,25)).string_("0.5").action_({|x| thresholds[i] = x.value.asFloat;});
};
win.onClose_({circle_buf.free;input_bus.free;osc_func.clear;analysis_synth.free;});
}.defer;
}.play;
)
// thanks to Ted Moore for the SC code cleaning and improvments!

@ -40,16 +40,16 @@ SynthDef(\playa, { arg output = 0, bufnum = 0;
// instantiate the player // instantiate the player
x = Synth(\playa,[\output, g.index, \bufnum, e.bufnum]); x = Synth(\playa,[\output, g.index, \bufnum, e.bufnum]);
// instantiate the processor // instantiate the processor, please ignore the Buffer UGen warnings
y = Synth(\becauseIcan,[\bufnum, b.bufnum, \nmfa, c.bufnum, \nmfb, d.bufnum, \input, g.index, \env, h.bufnum], x, 'addAfter'); y = Synth(\becauseIcan,[\bufnum, b.bufnum, \nmfa, c.bufnum, \nmfb, d.bufnum, \input, g.index, \env, h.bufnum], x, 'addAfter');
// instantiate the listener to cue the processing from the language side // instantiate the listener to cue the processing from the language side
( (
w = OSCFunc({ arg msg; w = OSCFunc({ arg msg;
if(msg[3]== 1, { if(msg[3]== 1, {
FluidBufNMF.process(s, b, numFrames: 22500, destination: c.bufnum, iterations: 3, fftSize: 1024, windowSize: 512, hopSize: 256); FluidBufNMF.process(s, b, numFrames: 22500, resynth: c.bufnum, components: 3, fftSize: 1024, windowSize: 512, hopSize: 256);
}, { }, {
FluidBufNMF.process(s, b, 22050, 22500, destination: d.bufnum, iterations: 3, fftSize: 1024, windowSize: 512, hopSize: 256); FluidBufNMF.process(s, b, 22050, 22500, resynth: d.bufnum, components: 3, fftSize: 1024, windowSize: 512, hopSize: 256);
});}, '/processplease', s.addr); });}, '/processplease', s.addr);
) )

@ -0,0 +1,74 @@
// using NMF, splitting a small portion, then associating components to targets, then thresholding on these target's activations to find objects.
//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, components:10);
c.query;
}.play;
)
// wait for the query to print
// 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 =1;
{PlayBuf.ar(10,c)[~dog]}.play
)
(
~bird = 3;
{PlayBuf.ar(10,c)[~bird]}.play
)
// 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);
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;
)

@ -0,0 +1,51 @@
// Using an 88-components piano base to do polyphonic pitch tracking
//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');
)

@ -0,0 +1,59 @@
(
b = Buffer.read(s,File.realpath(FluidBufNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
c = Buffer.new(s);
)
(
// with basic params
Routine{
var startTime, target, tolerance, startThresh, prevThresh, curThresh, curVal, prevVal, iters, maxIters, dVal, dThresh;
startTime = Main.elapsedTime;
prevThresh = 0.1; //initial threshold (between 0.00001 and 0.99999
target = 10; //number of slices desired
tolerance = 0; // the acceptable error in the number of slices yield
maxIters = 100; //max number of iterations acceptable
//makes a first iteration
FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh,action:{|x|prevVal = x.numFrames});
//makes a second iteration
if ( (prevVal < target), {
curThresh = (prevThresh * 0.5).max(0.000001);
}, {
curThresh = (prevThresh * 2).min(0.999999);
});
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
//makes further iterations until the result is achieved, or the maximum of acceptable iterations is reached
iters = 2;
while ( {
(iters < maxIters) && ((curVal - target).abs > tolerance)
}, {
iters = iters + 1;
dVal = curVal - prevVal;
dThresh = curThresh - prevThresh;
prevThresh = curThresh;
prevVal = curVal;
if ( (dVal == 0), {
//if we have not change results between the last 2 passes, make the variation of threshold bigger
curThresh = (dThresh + curThresh).min(0.999999).max(0.000001);
},{
//if we have
curThresh = (((dThresh / dVal) * (target - curVal)) + curThresh).min(0.999999).max(0.000001);
});
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
}
);
//depending on the outcome, gives the right info back
if ( iters >= maxIters, {
// failed
"Failed to find a suitable threshold in % seconds.\n".postf((Main.elapsedTime - startTime).round(0.01));
}, {
// succeeded
"Found % as a suitable threshold for % slices in % seconds and % iterations.\n".postf(curThresh, curVal, (Main.elapsedTime - startTime).round(0.01), iters);
}
);
}.play
)

@ -0,0 +1,136 @@
TITLE:: FluidAmpGate
SUMMARY:: Amplitude-based Gating 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 amplitude changes as onsets and offsets. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
FluidAmpSlice is based on an envelop follower on a highpassed version of the signal, which is then going through a Schmidt trigger and state-aware time contraints. The example code below is unfolding the various possibilites in order of complexity.
The process will return an audio steam with square envelopes around detected slices the different slices, where 1s means in slice and 0s mean in silence.
CLASSMETHODS::
METHOD:: ar
The audio rate version of the object.
ARGUMENT:: in
The audio to be processed.
ARGUMENT:: rampUp
The number of samples the absolute envelope follower will take to reach the next value when raising.
ARGUMENT:: rampDown
The number of samples the absolute envelope follower will take to reach the next value when falling.
ARGUMENT:: onThreshold
The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state.
ARGUMENT:: offThreshold
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:: highPassFreq
The frequency of the fourth-order LinkwitzRiley 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.
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: absThresh sanity
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12);
[source, env]
}.plot(0.1);
)
//basic tests: absThresh hysteresis
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -16);
[source, env]
}.plot(0.1);
)
//basic tests: absThresh min slice
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSliceLength:441);
[source, env]
}.plot(0.1);
)
//basic tests: absThresh min silence
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSilenceLength:441);
[source, env]
}.plot(0.1);
)
//mid tests: absThresh time hysteresis on
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthAbove:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: absThresh time hysteresis off
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthBelow:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: absThresh with lookBack
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: absThresh with lookAhead
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookAhead:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: absThresh with asymetrical lookBack and lookAhead
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:221, lookAhead:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//drum slicing, many ways
//load a buffer
b = Buffer.read(s,File.realpath(FluidAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
//have fun with a gate (explore lookahead and lookback, but correct for latency, which will be the greatest of the lookahead and lookback)
(
{var env, source = PlayBuf.ar(1,b);
env = FluidAmpGate.ar(source, rampUp:441, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:20);
[DelayN.ar(source,delaytime:441/44100), env]
}.plot(2, separately:true);
)
::

@ -1,14 +1,14 @@
TITLE:: FluidAmpSlice TITLE:: FluidAmpSlice
SUMMARY:: Amplitude-based Slicer SUMMARY:: Amplitude-based Detrending Slicer
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements an amplitude-based slicer, with various customisable options and conditions to detect relative amplitude changes as onsets. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
FluidAmpSlice is based on two envelop followers on a highpassed version of the signal: one 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. FluidAmpSlice is based on two envelop followers on a highpassed version of the signal: one slow that gives the trend, and one fast. Each have features that will interact. The example code below is unfolding the various possibilites in order of complexity.
The process will return an audio steam with square envelopes around detected slices the different slices, where 1s means in slice and 0s mean in silence. The process will return an audio steam with sample-long impulses at estimated starting points of the different slices.
CLASSMETHODS:: CLASSMETHODS::
@ -18,166 +18,68 @@ METHOD:: ar
ARGUMENT:: in ARGUMENT:: in
The audio to be processed. The audio to be processed.
ARGUMENT:: absRampUp ARGUMENT:: fastRampUp
The number of samples the fast envelope follower will take to reach the next value when raising. Typically, this will be faster than slowRampUp.
ARGUMENT:: fastRampDown
The number of samples the fast envelope follower will take to reach the next value when falling. Typically, this will be faster than slowRampDown.
ARGUMENT:: slowRampUp
The number of samples the absolute envelope follower will take to reach the next value when raising. The number of samples the absolute envelope follower will take to reach the next value when raising.
ARGUMENT:: absRampDown ARGUMENT:: slowRampDown
The number of samples the absolute envelope follower will take to reach the next value when falling. The number of samples the absolute envelope follower will take to reach the next value when falling.
ARGUMENT:: absThreshOn ARGUMENT:: onThreshold
The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state. The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers.
ARGUMENT:: offThreshold
The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again.
ARGUMENT:: absThreshOff ARGUMENT:: floor
The threshold in dB of the absolute envelope follower to trigger an offset, , aka to go ON when in OFF state. The level in dB the slowRamp needs to be above to consider a detected difference valid, allowing to ignore the slices in the noise floor.
ARGUMENT:: minSliceLength ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. 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 ARGUMENT:: highPassFreq
The frequency of the fourth-order LinkwitzRiley 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. The frequency of the fourth-order LinkwitzRiley 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.
RETURNS:: 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))::. An audio stream with square envelopes around the slices. The latency between the input and the output is dependant on the relation between the two envelope followers.
EXAMPLES:: EXAMPLES::
code:: code::
//basic tests: absThresh sanity // detrending explained
( // Our source here is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12);
[source, env]
}.plot(0.1);
)
//basic tests: absThresh hysteresis
( (
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); {var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -16); env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60);
[source, env] [source, env]
}.plot(0.1); }.plot(0.08);
) )
//basic tests: absThresh min slice //beware of double trigger at the begining of the 2nd cycle above). A solution: Schmidth triggers
( (
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); {var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSliceLength:441); env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60);
[source, env] [source, env]
}.plot(0.1); }.plot(0.08);
) )
//basic tests: absThresh min silence // another solution: minslicelength
( (
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); {var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSilenceLength:441); env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 220);
[source, env] [source, env]
}.plot(0.1); }.plot(0.08);
)
//mid tests: absThresh time hysteresis 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);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: absThresh time hysteresis 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 //quick drum onsets
(
{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 hysteresis, 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.05);
)
//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.05);
)
//drum slicing, many ways
//load a buffer //load a buffer
b = Buffer.read(s,File.realpath(FluidAmpSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); b = Buffer.read(s,File.realpath(FluidAmpSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
//have fun with a gate (explore lookahead and lookback, but correct for latency)
( (
{var env, source = PlayBuf.ar(1,b); {var env, source = PlayBuf.ar(1,b);
env = FluidAmpSlice.ar(source,absRampUp:44, absRampDown:2205, absThreshOn:-20, absThreshOff: -23, minSilenceLength:1100, lookBack:441); env = FluidAmpSlice.ar(source,fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410,onThreshold: 10,offThreshold: 5,floor: -40,minSliceLength: 4410,highPassFreq: 20);
[DelayN.ar(source,delaytime:441/44100), env] [source, env]
}.plot(2,separately:true);
)
(
{var env, source = PlayBuf.ar(1,b);
env = FluidAmpSlice.ar(source,highPassFreq:120, absRampUp:4410, absRampDown:4410, absThreshOn:-60, absThreshOff: -60, relRampUp:10, relRampDown:2205, relThreshOn:13, relThreshOff:10, minSilenceLength:4410, highPassFreq:20);
[source, Trig.ar(env,0)]
}.play; }.play;
) )
:: ::

@ -0,0 +1,196 @@
TITLE:: FluidBufAmpGate
SUMMARY:: Amplitude-based Gating Slicer for Buffers
CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute amplitude changes as onsets and offsets. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
FluidBufAmpGate is based on an envelop follower on a highpassed version of the signal, which is then going through a Schmidt trigger and state-aware time contraints. The example code below is unfolding the various possibilites in order of complexity.
The process will return a two-channel buffer with the addresses of the onset on the first channel, and the address of the offset on the second channel.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed.
ARGUMENT:: startFrame
Where in the srcBuf should the slicing process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel sources, which channel should be processed.
ARGUMENT:: numChans
For multichannel sources, how many channel should be summed.
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
ARGUMENT:: rampUp
The number of samples the absolute envelope follower will take to reach the next value when raising.
ARGUMENT:: rampDown
The number of samples the absolute envelope follower will take to reach the next value when falling.
ARGUMENT:: onThreshold
The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state.
ARGUMENT:: offThreshold
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:: highPassFreq
The frequency of the fourth-order LinkwitzRiley 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:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
EXAMPLES::
code::
// define a test signal and a destination buffer
(
b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs}));
c = Buffer.new(s);
)
b.play
b.plot
//basic tests: absThresh sanity
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: absThresh hysteresis
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -16)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: absThresh min slice
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSliceLength:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: absThresh min silence
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSilenceLength:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh time hysteresis on
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthAbove:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh time hysteresis off
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthBelow:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh with lookBack
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh with lookAhead
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookAhead:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh with asymetrical lookBack and lookAhead
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:221, lookAhead:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
::
STRONG::A musical example.::
CODE::
//load a buffer
(
b = Buffer.read(s,File.realpath(FluidBufAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
c = Buffer.new(s);
)
// slice the samples
FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//reformatting to read the onsets and offsets as pairs
c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});})
//loops over a splice with the MouseX, taking the respective onset and offset of a given slice
(
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(2, c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0],
BufRd.kr(2, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1],
BufRd.kr(2,c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0]
), 0, 1);
}.play;
)
::
STRONG::A stereo buffer example.::
CODE::
// make a stereo buffer
b = Buffer.alloc(s,88200,2);
// add some stereo clicks and listen to them
((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)})
b.play
// create a new buffer as destinations
c = Buffer.new(s);
//run the process on them
(
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30);
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed. The two channels of the output, respectively onset and offset indices, are interleaved as this is the SuperCollider buffer data formatting
c.getn(0,c.numFrames*2,{|item|(item*2).postln;})
// a more readable version: deinterleave onsetand offset
c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| (x*2).postln});})
::

@ -1,14 +1,14 @@
TITLE:: FluidBufAmpSlice TITLE:: FluidBufAmpSlice
SUMMARY:: Amplitude-based Slicer for Buffers SUMMARY:: Amplitude-based Detrending Slicer for Buffers
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements an amplitude-based slicer,with various customisable options and conditions to detect relative amplitude changes as onsets. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
FluidAmpSlice is based on two envelop followers on a highpassed version of the signal: one 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. FluidBufAmpSlice is based on two envelop followers on a highpassed version of the signal: one slow that gives the trend, and one fast. Each have features that will interact. The example code below is unfolding the various possibilites in order of complexity.
The process will return a two-channel buffer with the addresses of the onset on the first channel, and the address of the offset on the second channel. The process will return a buffer which contains indices (in sample) of estimated starting points of different slices.
STRONG::Threading:: STRONG::Threading::
@ -40,48 +40,30 @@ ARGUMENT:: numChans
ARGUMENT:: indices 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. 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:: absRampUp ARGUMENT:: fastRampUp
The number of samples the fast envelope follower will take to reach the next value when raising. Typically, this will be faster than slowRampUp.
ARGUMENT:: fastRampDown
The number of samples the fast envelope follower will take to reach the next value when falling. Typically, this will be faster than slowRampDown.
ARGUMENT:: slowRampUp
The number of samples the absolute envelope follower will take to reach the next value when raising. The number of samples the absolute envelope follower will take to reach the next value when raising.
ARGUMENT:: absRampDown ARGUMENT:: slowRampDown
The number of samples the absolute envelope follower will take to reach the next value when falling. The number of samples the absolute envelope follower will take to reach the next value when falling.
ARGUMENT:: absThreshOn ARGUMENT:: onThreshold
The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state. The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers.
ARGUMENT:: offThreshold
The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again.
ARGUMENT:: absThreshOff ARGUMENT:: floor
The threshold in dB of the absolute envelope follower to trigger an offset, , aka to go ON when in OFF state. The level in dB the slowRamp needs to be above to consider a detected difference valid, allowing to ignore the slices in the noise floor.
ARGUMENT:: minSliceLength ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. 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 ARGUMENT:: highPassFreq
The frequency of the fourth-order LinkwitzRiley 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. The frequency of the fourth-order LinkwitzRiley 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.
@ -94,73 +76,30 @@ RETURNS::
EXAMPLES:: EXAMPLES::
code:: code::
// detrending explained
// define a test signal and a destination buffer // define a test signal and a destination buffer
( (
b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs})); b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * ((((79000-i) % 22050).abs / 28000.0) + 0.2)}));
c = Buffer.new(s); c = Buffer.new(s);
) )
// the source is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track
b.play b.play
b.plot b.plot
//basic tests: absThresh sanity //
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12) FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60);
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: absThresh hysteresis
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -16)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: absThresh min slice
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSliceLength:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: absThresh min silence
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSilenceLength:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh time hysteresis on
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minLengthAbove:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh time hysteresis off
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minLengthBelow:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh with lookBack
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, lookBack:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh with lookAhead
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, lookAhead:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: absThresh with asymetrical lookBack and lookAhead
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, lookBack:221, lookAhead:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//advanced tests: absThresh hysteresis, long tail
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-60, absThreshOff: -60)
c.query c.query
c.getn(0,c.numFrames*2,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|item.postln;})
//solution: have to recut with relThresh //beware of multiple triggers at the begining of the 2nd cycle above). A solution: Schmidth triggers
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-60, absThreshOff: -60, relRampUp:5, relRampDown:220, relThreshOn:2, relThreshOff:1) FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60);
c.query c.query
c.getn(0,c.numFrames*2,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|item.postln;})
//beware of double trig. a solution: minSliceLength // we got most of them sorted, but there is another solution: minslicelength
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-60, absThreshOff: -60, relRampUp:5, relRampDown:220, relThreshOn:2, relThreshOff:1, minSliceLength:4410) FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 500);
c.query c.query
c.getn(0,c.numFrames*2,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|item.postln;})
:: ::
STRONG::A musical example.:: STRONG::A musical example.::
@ -172,25 +111,22 @@ c = Buffer.new(s);
) )
// slice the samples // slice the samples
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-70, absThreshOff: -80, relRampUp:10, relRampDown:441, relThreshOn:14, relThreshOff:12, minSliceLength:4410) FluidBufAmpSlice.process(s,b,indices:c,fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410,onThreshold: 10,offThreshold: 5,floor: -40,minSliceLength: 4410,highPassFreq: 20);
c.query c.query
c.getn(0,c.numFrames*2,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|item.postln;})
//reformatting to read the onsets and offsets as pairs
c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});})
//loops over a splice with the MouseX, taking the respective onset and offset of a given slice //loops over a splice with the MouseX, taking the respective onset and offset of a given slice
( (
{ {
BufRd.ar(1, b, BufRd.ar(1, b,
Phasor.ar(0,1, Phasor.ar(0,1,
BufRd.kr(2, c, BufRd.kr(1, c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0], MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1),
BufRd.kr(2, c, BufRd.kr(1, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1], MouseX.kr(1, BufFrames.kr(c)), 0, 1),
BufRd.kr(2,c, BufRd.kr(1,c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0] MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1);
), 0, 1); }.play;
}.play;
) )
:: ::
@ -211,11 +147,11 @@ c = Buffer.new(s);
// with basic params // with basic params
Routine{ Routine{
t = Main.elapsedTime; t = Main.elapsedTime;
FluidBufAmpSlice.process(s,b, indices: c, absRampUp:1, absRampDown:1, absThreshOn:-60, absThreshOff:-60); FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5);
(Main.elapsedTime - t).postln; (Main.elapsedTime - t).postln;
}.play }.play
) )
// list the indicies of detected attacks - the two input channels have been summed. The two channels of the output, respectively onset and offset indices, are interleaved as this is the SuperCollider buffer data formatting // list the indicies of detected attacks - the two input channels have been summed.
c.getn(0,c.numFrames*2,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|(item * 2).postln;})
:: ::

@ -4,8 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/Buffer RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/Buffer
DESCRIPTION:: DESCRIPTION::
A FluidBufCompose object provides a flexible utility for combining the contents of buffers on the server. It can be used for thing like mixing down multichannel buffers, or converting from left-right stereo to mid-side. It is used extensively in all the example code of LINK::Guides/FluidDecomposition:: as part of the FluCoMa project. footnote:: A FluidBufCompose object provides a flexible utility for combining the contents of buffers on the server. It can be used for thing like mixing down multichannel buffers, or converting from left-right stereo to mid-side. It is used extensively in all the example code of LINK::Guides/FluidDecomposition:: as part of the FluCoMa project. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
At its most simple, the object copies the content of a source buffer into a destination buffer. The flexibility comes from the various flags controlling which portions and channels of the source to use, and by applying gains (which can be positive or negative) to the source data and the portion of the destination that would be overwritten. At its most simple, the object copies the content of a source buffer into a destination buffer. The flexibility comes from the various flags controlling which portions and channels of the source to use, and by applying gains (which can be positive or negative) to the source data and the portion of the destination that would be overwritten.
@ -94,74 +93,4 @@ FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartCh
FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0); FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0);
d.query; d.query;
d.play; d.play;
::
STRONG::A more complex example: using composition as an Mid-Side filtering process::
CODE::
// load a stereo buffer and initialise the many destinations
(
b = Buffer.read(s,File.realpath(FluidBufCompose.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav");
c = Buffer.new(s);
d = Buffer.new(s);
e = Buffer.new(s);
f = Buffer.new(s);
)
// encode the mid (in c) and the side (in d)
(
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, destination: c);
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, destination: d);
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, startChan: 1, destination: c, destGain: 1.0);
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp * -1.0, startChan: 1, destination: d, destGain: 1.0);
)
// (optional) compare auraly the stereo with the MS
b.play;
{PlayBuf.ar(1,[c,d])}.play;
// The geeky bit: copy the side (buffer d) on itself with specific amplitudes and delays, in effect applying a FIR filter through expensive convolution
// Important: do either of the 3 options below
// option 1: apply a high pass on the side, with a cutoff of nyquist / 4
e.free; e = Buffer.new(s);
(
[1.0, -1.0].do({ arg x,y;
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
});
)
// option 2: apply a high pass on the side, with a cutoff of nyquist / 10
e.free; e = Buffer.new(s);
(
[0.8, -0.32, -0.24, -0.16, -0.08].do({ arg x,y;
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
});
)
// option 3: apply a high pass on the side, with a cutoff of nyquist / 100
e.free; e = Buffer.new(s);
(
[0.982494, -0.066859, -0.064358, -0.061897, -0.059477, -0.057098, -0.054761, -0.052466, -0.050215, -0.048007, -0.045843, -0.043724, -0.041649, -0.03962, -0.037636, -0.035697, -0.033805, -0.031959, -0.030159, -0.028406, -0.026699, -0.025038, -0.023425, -0.021857, -0.020337].do({ arg x,y;
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
});
)
// play the high-passed side buffer
e.play;
// if you want to try the other filters, do not forget to clear the destination buffer since it will add programmatically onto itself and would not create the expected frequency response
// decode the MS back to stereo
(
FluidBufCompose.process(s,c, numChans: 2, gain: -3.0.dbamp, destination: f);
FluidBufCompose.process(s,e, gain: -3.0.dbamp, destination: f, destGain: 1.0);
FluidBufCompose.process(s,e, gain: -3.0.dbamp * -1.0, destination: f, destStartChan: 1, destGain: 1.0);
)
// play the MS processed version
f.play;
// compare with the original
b.play;
:: ::

@ -16,11 +16,7 @@ The algorithm takes a buffer in, and divides it into two or three outputs, depen
## a percussive component; ## a percussive component;
## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.:: ## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.::
It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).
::
More information on median filtering, and on HPSS for musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file.
STRONG::Threading:: STRONG::Threading::
@ -101,10 +97,10 @@ 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 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 ARGUMENT:: hopSize
The window hop size in samples. As HPSS relies on spectral frames, we need to move the window forward. It can be any size but low overlap may create audible artefacts. The window hop size in samples. As HPSS relies on spectral frames, we need to move the window forward. It can be any size but low overlap may create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: action ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [harmonic, percussive, residual] as an argument. A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [harmonic, percussive, residual] as an argument.

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreadi
DESCRIPTION:: DESCRIPTION::
This class implements two loudness descriptors, computing the true peak of the signal as well as applying the filters proposed by broadcasting standards to emulate the perception of amplitude. It is part of the 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 Unions 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 LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a multichannel buffer with two channels per input channel, one for loudness and one for the true peak value of the frame, both in dBfs. More information on broadcasting standardisation of loudness measurement is available at the reference page FOOTNOTE::https://tech.ebu.ch/docs/tech/tech3341.pdf:: and in more musician-friendly explantions here FOOTNOTE::http://designingsound.org/2013/02/06/loudness-and-metering-part-1/::. Each sample represents a value, which is every hopSize. Its sampling rate is STRONG::sourceSR / hopSize::. 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::.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufMelBands RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufMelBands
DESCRIPTION:: DESCRIPTION::
This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoeffs:: number of cepstral coefficients. It has the 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 Unions 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 advantage of being amplitude invariant, except for the first coefficient. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a single multichannel buffer of STRONG::numCoeffs:: per input channel. Each frame represents a value, which is every hopSize. The process will return a single multichannel buffer of STRONG::numCoeffs:: per input channel. Each frame represents a value, which is every hopSize.
@ -54,10 +54,10 @@ 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 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 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 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 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 inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: action 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. 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.

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreadi
DESCRIPTION:: DESCRIPTION::
This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a single multichannel buffer of STRONG::numBands:: per input channel. Each frame represents a value, which is every hopSize. The process will return a single multichannel buffer of STRONG::numBands:: per input channel. Each frame represents a value, which is every hopSize.
@ -48,14 +48,17 @@ ARGUMENT:: minFreq
ARGUMENT:: maxFreq ARGUMENT:: maxFreq
The highest boundary of the highest band of the model, in Hz. The highest boundary of the highest band of the model, in Hz.
ARGUMENT:: normalize
This flag enables the scaling of the output to preserve the energy of the window. It is on (1) by default.
ARGUMENT:: windowSize 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 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 ARGUMENT:: hopSize
The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize 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 inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: action 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. 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.

@ -27,10 +27,7 @@ In this implementation, the components are reconstructed by masking the original
The whole process can be related to a channel vocoder where, instead of fixed bandpass filters, we get more complex filter shapes that are learned from the data, and the activations correspond to channel envelopes. The whole process can be related to a channel vocoder where, instead of fixed bandpass filters, we get more complex filter shapes that are learned from the data, and the activations correspond to channel envelopes.
More information on possible musicianly uses of NMF are availabe in LINK::Guides/FluCoMa:: overview file. FluidBufNMF is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
STRONG::Threading:: STRONG::Threading::
@ -59,7 +56,7 @@ ARGUMENT:: startChan
ARGUMENT:: numChans ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed. For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: destination ARGUMENT:: resynth
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. 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 ARGUMENT:: bases
@ -94,10 +91,10 @@ 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 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 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 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 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 inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: windowType ARGUMENT:: windowType
The inner FFT/IFFT windowing type (not implemented yet) The inner FFT/IFFT windowing type (not implemented yet)
@ -106,7 +103,7 @@ 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) 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 ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination, bases, activations] as an argument. A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [resynth, bases, activations] as an argument.
RETURNS:: RETURNS::
Nothing, as the various destination buffers are declared in the function call. Nothing, as the various destination buffers are declared in the function call.
@ -145,7 +142,7 @@ d.play //////(beware !!!! loud!!!)
( (
// separate them in 2 components // separate them in 2 components
Routine { Routine {
FluidBufNMF.process(s, d, destination:e, bases: f, activations:g, components:2); FluidBufNMF.process(s, d, resynth:e, bases: f, activations:g, components:2);
e.query; e.query;
f.query; f.query;
g.query; g.query;
@ -299,7 +296,7 @@ z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, des
//process the whole file, splitting it with the 2 trained bases //process the whole file, splitting it with the 2 trained bases
( (
Routine{ Routine{
FluidBufNMF.process(s, b, destination: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2); FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2);
~originalNMF.query; ~originalNMF.query;
}.play; }.play;
) )
@ -357,7 +354,7 @@ e.query
( (
// use the seeding basis, without updating // use the seeding basis, without updating
Routine { Routine {
FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 2, activations:g, components:3); FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3);
e.query; e.query;
f.query; f.query;
g.query; g.query;
@ -376,7 +373,7 @@ g.plot;
( (
// use the seeding bases, with updating this time // use the seeding bases, with updating this time
Routine { Routine {
FluidBufNMF.process(s, d, destination:f, bases: e, basesMode: 1, activations:g, components:3); FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3);
e.query; e.query;
f.query; f.query;
g.query; g.query;

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a non-real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer which contains indices (in sample) of estimated starting points of different slices. The process will return a buffer which contains indices (in sample) of estimated starting points of different slices.
@ -55,14 +55,17 @@ ARGUMENT:: threshold
ARGUMENT:: filterSize 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. The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes.
ARGUMENT:: minSliceLength
The minimum duration of a slice in number of hopSize.
ARGUMENT:: windowSize 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 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 ARGUMENT:: hopSize
The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize 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 inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: action 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 indices as an argument.
@ -81,10 +84,10 @@ c = Buffer.new(s);
) )
( (
// with basic params // with basic params, with a minimum slight length to avoid over
Routine{ Routine{
t = Main.elapsedTime; t = Main.elapsedTime;
FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.45); FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8);
(Main.elapsedTime - t).postln; (Main.elapsedTime - t).postln;
}.play }.play
) )
@ -122,15 +125,15 @@ FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filt
//check the number of slices: it is the number of frames in the transBuf minus the boundary index. //check the number of slices: it is the number of frames in the transBuf minus the boundary index.
c.query; c.query;
//play slice number 2 //play slice number 3
( (
{ {
BufRd.ar(1, b, BufRd.ar(1, b,
Line.ar( 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), 0, 1), BufRd.kr(1, c, DC.kr(4), 0, 1),
(BufRd.kr(1, c, DC.kr(3)) - BufRd.kr(1, c, DC.kr(2), 0, 1) + 1) / s.sampleRate), (BufRd.kr(1, c, DC.kr(4)) - BufRd.kr(1, c, DC.kr(3), 0, 1) + 1) / s.sampleRate),
0,1); 0,1);
}.play; }.play;
) )
@ -167,5 +170,5 @@ Routine{
) )
// list the indicies of detected attacks - the two input channels have been summed // list the indicies of detected attacks - the two input channels have been summed
c.getn(0,c.numFrames,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|(item * 2).postln;})
:: ::

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
This class implements many spectral-based onset detection metrics, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object yet not as offline processes. It is part of the 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements many spectral-based onset detection metrics, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object yet not as offline processes. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer which contains indices (in sample) of estimated starting points of different slices. The process will return a buffer which contains indices (in sample) of estimated starting points of different slices.
@ -72,7 +72,7 @@ ARGUMENT:: hopSize
The window hop size. As spectral differencing relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: action 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 indices as an argument.
@ -134,11 +134,11 @@ c = Buffer.new(s);
// with basic params // with basic params
Routine{ Routine{
t = Main.elapsedTime; t = Main.elapsedTime;
FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.1); FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001);
(Main.elapsedTime - t).postln; (Main.elapsedTime - t).postln;
}.play }.play
) )
// list the indicies of detected attacks - the two input channels have been summed // list the indicies of detected attacks - the two input channels have been summed
c.getn(0,c.numFrames,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|(item * 2).postln;})
:: ::

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreadi
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. 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 pitch and one for the pitch tracking confidence. Each sample represents a value, which is every hopSize. Its sampling rate is sourceSR / hopSize.
@ -60,10 +60,10 @@ 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 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 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 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 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 inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: action 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. 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.

@ -5,13 +5,13 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreadi
DESCRIPTION:: DESCRIPTION::
This class triggers a Sinusoidal Modelling process on buffers on the non-real-time thread of the server. It implements a mix and match algorithms taken from classic papers. 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class triggers a Sinusoidal Modelling process on buffers on the non-real-time thread of the server. It implements a mix of algorithms taken from classic papers. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The algorithm will take a buffer in, and will divide it in two parts: LIST:: The algorithm will take a buffer in, and will divide it in two parts: LIST::
## a reconstruction of what it detects as sinusoidal; ## a reconstruction of what it detects as sinusoidal;
## a residual derived from the previous buffer to allow null-summing:: ## a residual derived from the previous buffer to allow null-summing::
The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. More information on this model, and on how it links to musicianly thinking, are availabe in LINK::Guides/FluCoMa:: overview file. The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track.
STRONG::Threading:: STRONG::Threading::
@ -47,28 +47,40 @@ ARGUMENT:: residual
The index of the buffer where the residual of the sinusoidal component will be reconstructed. The index of the buffer where the residual of the sinusoidal component will be reconstructed.
ARGUMENT:: bandwidth ARGUMENT:: bandwidth
The width in bins of the fragment of the fft window that is considered a normal deviation for a potential continuous sinusoidal track. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. It is capped at (fftSize / 2) + 1. The number of bins used to resynthesises a peak. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. It is capped at (fftSize / 2) + 1.
ARGUMENT:: threshold ARGUMENT:: detectionThreshold
The normalised threshold, between 0 an 1, to consider a peak as a sinusoidal component from the normalized cross-correlation. The threshold in dB above which a magnitude peak is considered to be a sinusoidal component.
ARGUMENT:: birthLowThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the low end of the spectrum. It is interpolated across the spectrum until birthHighThreshold at half-Nyquist.
ARGUMENT:: birthHighThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the high end of the spectrum. It is interpolated across the spectrum until birthLowThreshold at DC.
ARGUMENT:: minTrackLen ARGUMENT:: minTrackLen
The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove space-monkeys, but is more CPU intensive and might reject quick pitch material. The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefacts, but is more CPU intensive and might reject quick pitch material.
ARGUMENT:: trackingMethod
The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive "Hungarian" one. footnote::Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018.::
ARGUMENT:: trackMagRange
The amplitude difference allowed for a track to diverge between frames, in dB.
ARGUMENT:: magWeight ARGUMENT:: trackFreqRange
The weight of the magnitude proximity of a peak when trying to associate it to an existing track (relative to freqWeight - suggested between 0 to 1) The frequency difference allowed for a track to diverge between frames, in Hertz.
ARGUMENT:: freqWeight ARGUMENT:: trackProb
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) The probability of the tracking algorithm to find a track.
ARGUMENT:: windowSize 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 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 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 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 ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to the highest of windowSize and (bandwidth - 1) * 2. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: action ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [sines, residual] as an argument. A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [sines, residual] as an argument.
@ -123,7 +135,7 @@ d = Buffer.new(s); e = Buffer.new(s);
( (
Routine{ Routine{
t = Main.elapsedTime; t = Main.elapsedTime;
FluidBufSines.process(s, b, sines: d, residual:e, threshold:0.3); FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384);
(Main.elapsedTime - t).postln; (Main.elapsedTime - t).postln;
}.play }.play
) )

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreadi
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The descriptors are: The descriptors are:
LIST:: LIST::
@ -59,10 +59,10 @@ 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 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 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 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 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 inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: action 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. 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.

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreadi
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements non-real-time statistical analysis on buffer channels. Typically, a buffer would hold various time series (i.e. descriptors over time), and link::Classes/FluidBufStats:: allows this series to be described statistically. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process returns a buffer where each channel of the STRONG::source:: buffer has been reduced to 7 statistics: mean, standard deviation, skewness, kurtosis, followed by 3 percentiles, by default the minimum value, the median, and the maximum value. Moreover, it is possible to request the same 7 stats to be applied to derivative of the input. These are useful to describe statistically the rate of change of the time series. The STRONG::stats:: buffer will grow accordingly, yielding the seven same statistical description of the n requested derivatives. Therefore, the STRONG::stats:: buffer will have as many channel as the input buffer, and as many frames as 7 times the requested STRONG::numDerivs::. 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::.

@ -4,7 +4,9 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
This class implements a simple tutorial object to illustrate and experiment with the behaviour of the FluidBuf* objects. 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: It simply starts a process that will, upon completion of its task, write a single value to the destination buffer. This is the general behaviour of much of the CPU consuming FluidBuf* objects. In this case, as a toy example, the task is simply just wait and do nothing, to simulate a task that would actually take that long in the background. This class implements a simple tutorial object to illustrate and experiment with the behaviour of the FluidBuf* objects. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
It simply starts a process that will, upon completion of its task, write a single value to the destination buffer. This is the general behaviour of much of the CPU consuming FluidBuf* objects. In this case, as a toy example, the task is simply just wait and do nothing, to simulate a task that would actually take that long in the background.
The process will, after waiting for STRONG::time:: millisecond, return its delay lenght as a Float writen at index [0] of the specified destination buffer. The process will, after waiting for STRONG::time:: millisecond, return its delay lenght as a Float writen at index [0] of the specified destination buffer.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufTransients RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufTransients
DESCRIPTION:: DESCRIPTION::
This class implements a non-real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a non-real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer which contains indices (in sample) of estimated starting points of the different slices. The process will return a buffer which contains indices (in sample) of estimated starting points of the different slices.
@ -144,5 +144,5 @@ Routine{
) )
// list the indicies of detected attacks - the two input channels have been summed // list the indicies of detected attacks - the two input channels have been summed
c.getn(0,c.numFrames,{|item|item.postln;}) c.getn(0,c.numFrames,{|item|(item*2).postln;})
:: ::

@ -4,14 +4,13 @@ CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
This class triggers a transient extractor on buffers on the non-real-time thread of the server. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: This class triggers a transient extractor on buffers on the non-real-time thread of the server. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
The algorithm will take a buffer in, and will divide it in two outputs: LIST:: The algorithm will take a buffer in, and will divide it in two outputs: LIST::
## the transients, estimated from the signal and extracted from it; ## the transients, estimated from the signal and extracted from it;
## the remainder of the material, as estimated by the algorithm.:: ## the remainder of the material, as estimated by the algorithm.::
The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. More information on signal estimation, and on its musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file. The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient.
STRONG::Threading:: STRONG::Threading::

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition,Classes/UnaryOpFunction
DESCRIPTION:: DESCRIPTION::
This class implements a sanity test for the FluCoMa Real-Time Client Wrapper. 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a sanity test for the FluCoMa Real-Time Client Wrapper. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
CLASSMETHODS:: CLASSMETHODS::

@ -14,11 +14,8 @@ The algorithm takes an audio in, and divides it into two or three outputs, depen
## a percussive component; ## a percussive component;
## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.:: ## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.::
It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).
::
More information on median filtering, and on HPSS for musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file.
CLASSMETHODS:: CLASSMETHODS::
METHOD:: ar METHOD:: ar
@ -73,7 +70,7 @@ ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION:: DESCRIPTION::
This class implements two loudness descriptors, computing the true peak of the signal as well as applying the filters proposed by broadcasting standards to emulate the perception of amplitude. It is part of the 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 Unions 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 LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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/::. 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/::.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMelBands RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMelBands
DESCRIPTION:: DESCRIPTION::
This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoeffs:: number of cepstral coefficients. It has the 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 Unions 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 LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a multichannel control steam of 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. 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.
@ -38,7 +38,7 @@ ARGUMENT:: hopSize
The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMFCC RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMFCC
DESCRIPTION:: DESCRIPTION::
This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a multichannel control steam of size STRONG::maxNumBands::, which will be repeated if no change happens within the algorythm, i.e. when the hopSize is larger than the server's kr period. 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.
@ -28,6 +28,9 @@ ARGUMENT:: maxFreq
ARGUMENT:: maxNumBands 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. 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:: normalize
This flag enables the scaling of the output to preserve the energy of the window. It is on (1) by default.
ARGUMENT:: windowSize 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 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
@ -35,7 +38,7 @@ ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.
@ -59,7 +62,7 @@ a = MultiSliderView(w,Rect(10, 10, 600, 300)).elasticMode_(1).isFilled_(1);
//run the window updating routine. //run the window updating routine.
( (
~winRange = 100; ~winRange = 0.1;
r = Routine { r = Routine {
{ {
b.get({ arg val; b.get({ arg val;
@ -78,7 +81,7 @@ r = Routine {
( (
x = { x = {
var source = SinOsc.ar(LFTri.kr(0.1).exprange(80,800),0,0.1); var source = SinOsc.ar(LFTri.kr(0.1).exprange(80,800),0,0.1);
Out.kr(b,FluidMelBands.kr(source,maxNumBands:40) / 50); Out.kr(b,FluidMelBands.kr(source,maxNumBands:40));
source.dup; source.dup;
}.play; }.play;
) )
@ -99,7 +102,7 @@ x = {arg bands = 40, low = 20, high = 20000;
) )
//set the winRange to a more informative value //set the winRange to a more informative value
~winRange = 400; ~winRange = 0.05;
// observe the number of bands. The unused ones at the top are not updated // observe the number of bands. The unused ones at the top are not updated
x.set(\bands,20) x.set(\bands,20)
@ -107,7 +110,7 @@ x.set(\bands,20)
// back to the full range // back to the full range
x.set(\bands,40) x.set(\bands,40)
// focus all the bands on a mid range // focus all the bands on a mid range: nothing to see!
x.set(\low,320, \high, 800) x.set(\low,320, \high, 800)
// focusing on the low end shows the fft resolution issue. One could restart the analysis with a larger fft to show more precision // focusing on the low end shows the fft resolution issue. One could restart the analysis with a larger fft to show more precision
@ -125,12 +128,12 @@ STRONG::A musical example: a perceptually spread vocoder::
CODE:: CODE::
//load a source and define control bus for the resynthesis cluster //load a source and define control bus for the resynthesis cluster
( (
b = Bus.new(\control,0,40); b = Bus.control(s,40);
c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
d = Group.new; d = Group.new;
) )
//play the source and s //play the source and send the analysis on the
( (
x = { x = {
arg dry = 0.2; arg dry = 0.2;
@ -154,7 +157,7 @@ var stepMel = rangeMel / 41;
arg i; arg i;
var freqMel = (stepMel * (i +1)) + lowMel; var freqMel = (stepMel * (i +1)) + lowMel;
var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700; var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700;
{SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.0001))}.play(d,1,addAction:\addToTail); {SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.5))}.play(d,1,addAction:\addToTail);
}); });
) )
@ -167,7 +170,7 @@ d.free; x.free; b.free; c.free;
// the bus, source and group // the bus, source and group
( (
b = Bus.new(\control,0,40); b = Bus.control(s,40);
c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
d = Group.new; d = Group.new;
) )
@ -193,11 +196,10 @@ x = {
var stepMel = rangeMel / 41; var stepMel = rangeMel / 41;
var freqMel = (stepMel * (i +1)) + lowMel; var freqMel = (stepMel * (i +1)) + lowMel;
var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700; var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700;
SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.0001))}.play(d,1,addAction:\addToTail); SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.5))}.play(d,1,addAction:\addToTail);
}); });
) )
// free all // free all
d.free; x.free; b.free; c.free; d.free; x.free; b.free; c.free;
:: ::

@ -12,9 +12,7 @@ NMF has been a popular technique in signal processing research for things like s
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. 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 LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
CLASSMETHODS:: CLASSMETHODS::
@ -41,7 +39,7 @@ ARGUMENT:: hopSize
The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.

@ -12,9 +12,7 @@ NMF has been a popular technique in signal processing research for things like s
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. 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 LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
CLASSMETHODS:: CLASSMETHODS::
@ -41,7 +39,7 @@ ARGUMENT:: hopSize
The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.
@ -182,134 +180,8 @@ z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, des
) )
:: ::
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, components:10);
c.query;
}.play;
)
// wait for the query to print
// 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 =4;
{PlayBuf.ar(10,c)[~dog]}.play
)
(
~bird = 3;
{PlayBuf.ar(10,c)[~bird]}.play
)
// 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);
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:: STRONG::Strange Resonators::
CODE:: CODE::
//load the source and declare buffers/arrays //load the source and declare buffers/arrays

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION:: DESCRIPTION::
This class implements a real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. The process will return an audio steam with sample-long impulses at estimated starting points of the different slices.
@ -34,6 +34,9 @@ ARGUMENT:: threshold
ARGUMENT:: filterSize 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. The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes.
ARGUMENT:: minSliceLength
The minimum duration of a slice in number of hopSize.
ARGUMENT:: windowSize 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 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
@ -41,7 +44,7 @@ ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.
@ -53,8 +56,7 @@ ARGUMENT:: maxFilterSize
This cannot be modulated. This cannot be modulated.
RETURNS:: RETURNS::
An audio stream with impulses at detected transients. The latency between the input and the output is (windowSize + An audio stream with impulses at detected transients. The latency between the input and the output is STRONG::windowSize + (hopSize * (((kernelSize+1)/2) + (filterSize / 2))):: samples at minimum.
((((kernelSize - 1) / 2) + (filterSize - 1)) * hopSize) at maximum.
EXAMPLES:: EXAMPLES::
@ -63,18 +65,18 @@ code::
b = Buffer.read(s,File.realpath(FluidNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); b = Buffer.read(s,File.realpath(FluidNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
// basic param (the process add a latency of windowSize samples // basic param (the process add a latency of windowSize samples
{var sig = PlayBuf.ar(1,b,loop:1); [FluidNoveltySlice.ar(sig,0,11,0.3) * 0.5, DelayN.ar(sig, 1, (1024 +((((11 - 1) / 2) + (1 - 1)) * 512)) / s.sampleRate)]}.play {var sig = PlayBuf.ar(1,b,loop:1); [FluidNoveltySlice.ar(sig,0,11,0.33) * 0.5, DelayN.ar(sig, 1, (1024 + (512 * (((11+1)/2) + (1 / 2))))/ s.sampleRate, 0.2)]}.play
// other parameters // other parameters
{var sig = PlayBuf.ar(1,b,loop:1); [FluidNoveltySlice.ar(sig, 1, 31, 0.004, 4, 128, 64) * 0.5, DelayN.ar(sig, 1, (128 +((((5 - 1) / 2) + (4 - 1)) * 64))/ s.sampleRate)]}.play {var sig = PlayBuf.ar(1,b,loop:1); [FluidNoveltySlice.ar(sig, 1, 31, 0.0035, 4, 100, 128, 32) * 0.5, DelayN.ar(sig, 1, (128 + (32 * (((31+1)/2) + (4 / 2))))/ s.sampleRate,0.2)]}.play
// more musical trans-trigged autopan // More musical, novelty-trigged autopan
( (
{ {
var sig, trig, syncd, pan; var sig, trig, syncd, pan;
sig = PlayBuf.ar(1,b,loop:1); sig = PlayBuf.ar(1,b,loop:1);
trig = FluidNoveltySlice.ar(sig, 0, 11, 0.2, 4, 128); trig = FluidNoveltySlice.ar(sig, 0, 11, 0.25, 5, 1, 128, 32);
syncd = DelayN.ar(sig, 1, ( (128 +((((11 - 1) / 2) + (4 - 1)) * 64)) / s.sampleRate)); syncd = DelayN.ar(sig, 1, (128 + (32 * (((11+1)/2) + (5 / 2))))/ s.sampleRate);
pan = TRand.ar(-1,1,trig); pan = TRand.ar(-1,1,trig);
Pan2.ar(syncd,pan); Pan2.ar(syncd,pan);
}.play }.play

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION:: DESCRIPTION::
This class implements many spectral based onset detection metrics, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object. It is part of the 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements many spectral based onset detection metrics, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. The process will return an audio steam with sample-long impulses at estimated starting points of the different slices.
@ -51,7 +51,7 @@ ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.
@ -71,7 +71,7 @@ b = Buffer.read(s,File.realpath(FluidOnsetSlice.class.filenameSymbol).dirname.wi
// other parameters // other parameters
{var sig = PlayBuf.ar(1,b,loop:1); [FluidOnsetSlice.ar(sig, 2, 0.06, 55, 7, 0, 128, 64) * 0.5, DelayN.ar(sig, 1, (128)/ s.sampleRate)]}.play {var sig = PlayBuf.ar(1,b,loop:1); [FluidOnsetSlice.ar(sig, 2, 0.06, 55, 7, 0, 128, 64) * 0.5, DelayN.ar(sig, 1, (128)/ s.sampleRate)]}.play
// more musical trans-trigged autopan // More musical, onset-trigged autopan
( (
{ {
var sig, trig, syncd, pan; var sig, trig, syncd, pan;

@ -4,9 +4,9 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Pitch RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Pitch
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 [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:: CLASSMETHODS::
@ -40,7 +40,7 @@ ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition,Classes/UnaryOpFunction
DESCRIPTION:: DESCRIPTION::
This class implements a sanity test for the FluCoMa Real-Time Client FFT/IFFT Wrapper. 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a sanity test for the FluCoMa Real-Time Client FFT/IFFT Wrapper. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
CLASSMETHODS:: CLASSMETHODS::
@ -23,7 +23,7 @@ ARGUMENT:: hopSize
How much the buffered window moves forward, in samples. The -1 default value will default to half of windowSize (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 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. How large will the FFT be, zero-padding the buffer to the right size, which should be bigger than the windowSize argument, bigger than 4 samples, and should be a power of 2. This is a way to oversample the FFT for extra precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.

@ -4,13 +4,13 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION:: DESCRIPTION::
This class applies a Sinusoidal Modelling process on its audio input. It implements a mix and match algorithms taken from classic papers. 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class applies a Sinusoidal Modelling process on its audio input. It implements a mix of algorithms taken from classic papers. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The algorithm will take an audio in, and will divide it in two parts: LIST:: The algorithm will take an audio in, and will divide it in two parts: LIST::
## a reconstruction of what it detects as sinusoidal; ## a reconstruction of what it detects as sinusoidal;
## a residual derived from the previous signal to allow null-summing:: ## a residual derived from the previous signal to allow null-summing::
The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. More information on this model, and on how it links to musicianly thinking, are availabe in LINK::Guides/FluCoMa:: overview file. The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track.
CLASSMETHODS:: CLASSMETHODS::
@ -22,19 +22,31 @@ ARGUMENT:: in
The input to be processed The input to be processed
ARGUMENT:: bandwidth ARGUMENT:: bandwidth
The width in bins of the fragment of the fft window that is considered a normal deviation for a potential continuous sinusoidal track. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. It is capped at (fftSize / 2) + 1. The number of bins used to resynthesises a peak. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. It is capped at (fftSize / 2) + 1.
ARGUMENT:: threshold ARGUMENT:: detectionThreshold
The normalised threshold, between 0 an 1, to consider a peak as a sinusoidal component from the normalized cross-correlation. The threshold in dB above which a magnitude peak is considered to be a sinusoidal component.
ARGUMENT:: birthLowThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the low end of the spectrum. It is interpolated across the spectrum until birthHighThreshold at half-Nyquist.
ARGUMENT:: birthHighThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the high end of the spectrum. It is interpolated across the spectrum until birthLowThreshold at DC.
ARGUMENT:: minTrackLen ARGUMENT:: minTrackLen
The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove space-monkeys, but is more CPU intensive and might reject quick pitch material. The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefacts, but is more CPU intensive and might reject quick pitch material.
ARGUMENT:: trackingMethod
The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive "Hungarian" one. footnote::Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018.::
ARGUMENT:: magWeight ARGUMENT:: trackMagRange
The weight of the magnitude proximity of a peak when trying to associate it to an existing track (relative to freqWeight - suggested between 0 to 1) The amplitude difference allowed for a track to diverge between frames, in dB.
ARGUMENT:: freqWeight ARGUMENT:: trackFreqRange
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) The frequency difference allowed for a track to diverge between frames, in Hertz.
ARGUMENT:: trackProb
The propensity of peaks to become enrolled into tracks.
ARGUMENT:: windowSize 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 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
@ -43,7 +55,7 @@ ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to the highest of windowSize and (bandwidth - 1) * 2. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will default to the next power of 2 equal or above the highest of the windowSize and (bandwidth - 1) * 2.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.
@ -59,11 +71,17 @@ CODE::
b = Buffer.read(s,File.realpath(FluidSines.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav"); 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 // run with large parameters - left is sinusoidal model, right is residual
{FluidSines.ar(PlayBuf.ar(1,b,loop:1),threshold: 0.2, minTrackLen: 2, windowSize: 2048, fftSize: 8192)}.play {FluidSines.ar(PlayBuf.ar(1,b,loop:1),detectionThreshold: -40, minTrackLen: 2, windowSize: 2048, fftSize: 8192)}.play
// interactive parameters with a narrower bandwidth // interactive parameters with a narrower bandwidth
{FluidSines.ar(PlayBuf.ar(1,b,loop:1),30,MouseX.kr(), 5, windowSize: 1000, hopSize: 200, fftSize: 4096)}.play {FluidSines.ar(PlayBuf.ar(1,b,loop:1), 30, MouseX.kr(-140,-10),MouseY.kr(-110,-10),MouseY.kr(-140,-40), 10 , windowSize: 1000, hopSize: 200, fftSize: 4096)}.play
// null test (the process add a latency of (( hopSize * minTrackLen) + windowSize) samples // 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 {var sig = PlayBuf.ar(1,b,loop:1); [FluidSines.ar(sig).sum - DelayN.ar(sig, 1, ((( 512 * 15) + 1024)/ s.sampleRate))]}.play
// as the algorithm resynthesize the sinusoidal peaks, we would expect to get it to work almost perfectly on a sine wave, with these settings that tell the process to tolerate everything as a sinusoid, even short and quiet peaks
{FluidSines.ar(SinOsc.ar(mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 200,trackFreqRange: 1000,trackProb: 0)}.play;
// as this is a windowed process, the frequency of the peak is good for that full window, and therefore interesting artefacts appear when the pitch is changing.
{FluidSines.ar(SinOsc.ar(LFTri.kr(0.1).exprange(220,880),mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 300,trackFreqRange: 1000,trackProb: 0)}.play;
:: ::

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile
DESCRIPTION:: 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The descriptors are: The descriptors are:
LIST:: LIST::
@ -39,7 +39,7 @@ ARGUMENT:: hopSize
The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). 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 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. The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated. How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.

@ -4,9 +4,9 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION:: DESCRIPTION::
This class implements a real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the 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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements a real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. The process will return an audio steam with sample-long impulses at estimated starting points of the different slices.
CLASSMETHODS:: CLASSMETHODS::
@ -59,7 +59,7 @@ b = Buffer.read(s,File.realpath(FluidTransientSlice.class.filenameSymbol).dirnam
// other parameters // other parameters
{var sig = PlayBuf.ar(1,b,loop:1); [FluidTransientSlice.ar(sig,order:80,minSliceLength:2205) * 0.5, DelayN.ar(sig, 1, ((256 + 128 - 80)/ s.sampleRate))]}.play {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 // More musical, transient-trigged autopan
( (
{ {
var sig, trig, syncd, pan; var sig, trig, syncd, pan;

@ -4,14 +4,13 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION:: DESCRIPTION::
This class applies a real-time transient extractor on its input. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: This class applies a real-time transient extractor on its input. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the LINK:: Guides/FluidDecomposition:: of LINK:: Guides/FluCoMa::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
The algorithm will take an audio in, and will divide it in two outputs: LIST:: The algorithm will take an audio in, and will divide it in two outputs: LIST::
## the transients, estimated from the signal and extracted from it; ## the transients, estimated from the signal and extracted from it;
## the remainder of the material, as estimated by the algorithm. :: ## the remainder of the material, as estimated by the algorithm. ::
The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. More information on signal estimation, and on its musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file. The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient.
CLASSMETHODS:: CLASSMETHODS::

@ -4,8 +4,8 @@ categories:: Libraries>FluidDecomposition
related:: Classes/FluidBufNMF related:: Classes/FluidBufNMF
description:: description::
This is my description of the FluCoMa project. footnote:: The Fluid Corpus Manipulation project (FluCoMA) instigates new musical ways of exploiting ever-growing banks of sound and gestures within the digital composition process, by bringing breakthroughs of signal decomposition DSP and machine learning to the toolset of techno-fluent computer composers, creative coders and digital artists. The first set of tools released is the LINK:: Guides/FluidDecomposition::
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 Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
subsection:: stuff This was made possible thanks to a grant by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).
This is my subsection
For more information, including a forum and learning material, please visit the FluCoMa project website at http://www.flucoma.org/

@ -81,7 +81,7 @@ d = Bus.control(s,1);
) )
// start a very long job // start a very long job
e = {Out.kr(d,FluidBufNMF.kr(b, destination:c, components:50, iterations:1000, windowSize:8192, hopSize:256))}.play e = {Out.kr(d,FluidBufNMF.kr(b, resynth:c, components:50, iterations:1000, windowSize:8192, hopSize:256))}.play
// make a dummy synth to look at the progress // make a dummy synth to look at the progress
f = {In.kr(d).poll}.play f = {In.kr(d).poll}.play
@ -160,5 +160,3 @@ Routine{
}.play; }.play;
) )
:: ::

@ -10,6 +10,9 @@ subsection:: Slices
LINK:: Classes/FluidAmpSlice:: & LINK:: Classes/FluidAmpSlice:: &
LINK:: Classes/FluidBufAmpSlice:: LINK:: Classes/FluidBufAmpSlice::
LINK:: Classes/FluidAmpGate:: &
LINK:: Classes/FluidBufAmpGate::
Slice by amplitude envelope Slice by amplitude envelope
LINK:: Classes/FluidOnsetSlice:: & LINK:: Classes/FluidOnsetSlice:: &

@ -1,10 +1,28 @@
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
target_compile_features(${PLUGIN} PUBLIC cxx_std_14) target_compile_features(${PLUGIN} PRIVATE cxx_std_14)
if(MSVC) if(MSVC)
target_compile_options(${PLUGIN} PRIVATE /W4) foreach(flag_var
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if(${flag_var} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endif()
endforeach()
endif()
if(MSVC)
target_compile_options(${PLUGIN} PRIVATE /W3)
else() else()
target_compile_options(${PLUGIN} PRIVATE -Wall -Wextra -Wpedantic -Wreturn-type -Wconversion) target_compile_options(${PLUGIN} PRIVATE
-Wall -Wextra -Wpedantic -Wreturn-type -Wconversion -Wno-c++11-narrowing
)
endif() endif()
set_target_properties(${PLUGIN} PROPERTIES set_target_properties(${PLUGIN} PROPERTIES
@ -13,16 +31,23 @@ set_target_properties(${PLUGIN} PROPERTIES
CXX_EXTENSIONS NO CXX_EXTENSIONS NO
) )
if(APPLE)
set_target_properties(${PLUGIN} PROPERTIES
XCODE_GENERATE_SCHEME ON
)
#If we target 10.7 (actually < 10.9), we have to manually include this:
target_compile_options(${PLUGIN} PRIVATE -stdlib=libc++)
endif()
target_link_libraries( target_link_libraries(
${PLUGIN} ${PLUGIN}
PUBLIC PRIVATE
FLUID_DECOMPOSITION FLUID_DECOMPOSITION
# FLUID_MANIP
FLUID_SC_WRAPPER FLUID_SC_WRAPPER
PRIVATE HISSTools_FFT
FFTLIB
) )
target_include_directories( target_include_directories(
${PLUGIN} ${PLUGIN}
PRIVATE PRIVATE
@ -47,56 +72,32 @@ target_include_directories(
) )
get_property(HEADERS TARGET FLUID_DECOMPOSITION PROPERTY INTERFACE_SOURCES) get_property(HEADERS TARGET FLUID_DECOMPOSITION PROPERTY INTERFACE_SOURCES)
source_group(TREE "${FLUID_PATH}/include" FILES ${HEADERS}) source_group(TREE "${fluid_decomposition_SOURCE_DIR}/include" FILES ${HEADERS})
# get_property(HEADERS TARGET FLUID_MANIP PROPERTY INTERFACE_SOURCES)
if (SUPERNOVA) # source_group(TREE "${fluid_manipulation_SOURCE_DIR}/include" FILES ${HEADERS})
target_include_directories( #
${PLUGIN} # if (SUPERNOVA)
SYSTEM PRIVATE # target_include_directories(
"${SC_PATH}/external_libraries/nova-tt" # ${PLUGIN}
"${SC_PATH}/external_libraries/boost_lockfree" # SYSTEM PRIVATE
"${SC_PATH}/external_libraries/boost-lockfree" # "${SC_PATH}/external_libraries/nova-tt"
) # "${SC_PATH}/external_libraries/boost_lockfree"
endif() # "${SC_PATH}/external_libraries/boost-lockfree"
# )
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) # endif()
target_compile_options(${PLUGIN} PRIVATE -fvisibility=hidden)
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_options(
${PLUGIN}
PRIVATE
$<$<NOT:$<CONFIG:DEBUG>>: -mavx -msse -msse2 -msse3 -msse4>
)
endif()
if(MINGW) if(MINGW)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mstackrealign") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mstackrealign")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mstackrealign") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mstackrealign")
endif() endif()
if(MSVC) if(DEFINED FLUID_ARCH)
target_compile_options(${PLUGIN} PRIVATE /arch:AVX -D_USE_MATH_DEFINES) target_compile_options(${PLUGIN} PRIVATE ${FLUID_ARCH})
else(MSVC) endif()
target_compile_options(
${PLUGIN} PRIVATE $<$<NOT:$<CONFIG:DEBUG>>: -mavx -msse -msse2 -msse3 -msse4>
)
endif(MSVC)
####### added the fluid_decomposition
if(SUPERNOVA) if(MSVC)
add_library(${PLUGIN}_supernova MODULE ${FILENAME}) target_compile_options(${PLUGIN} PRIVATE -D_USE_MATH_DEFINES)
set_property(TARGET ${PROJECT}_supernova else()
PROPERTY COMPILE_DEFINITIONS SUPERNOVA) target_compile_options(${PLUGIN} PRIVATE -fvisibility=hidden)
endif() endif()

@ -0,0 +1,21 @@
# Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
add_library(
${PLUGIN}
MODULE
${FILENAME}
)
include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -0,0 +1,22 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/AmpGateClient.hpp>
#include <FluidSCWrapper.hpp>
static InterfaceTable *ft;
PluginLoad(FluidSTFTUGen)
{
ft = inTable;
using namespace fluid::client;
makeSCWrapper<AmpGateClient>("FluidAmpGate", ft);
}

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,7 +1,15 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) #include <clients/rt/AmpSliceClient.hpp>
#include <clients/rt/AmpSlice.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;

@ -0,0 +1,21 @@
# Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
add_library(
${PLUGIN}
MODULE
${FILENAME}
)
include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -0,0 +1,22 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/AmpGateClient.hpp>
#include <FluidSCWrapper.hpp>
static InterfaceTable *ft;
PluginLoad(OfflineFluidDecompositionUGens)
{
ft = inTable;
using namespace fluid::client;
makeSCWrapper<NRTThreadedAmpGateClient>("FluidBufAmpGate", ft);
}

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,8 +1,15 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
// FD_BufHPSS, an NRT buffer HPSS Processor #include <clients/rt/AmpSliceClient.hpp>
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899)
#include <clients/rt/AmpSlice.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;
@ -11,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{ {
ft = inTable; ft = inTable;
using namespace fluid::client; using namespace fluid::client;
makeSCWrapper<NRTThreadedAmpSlice>("FluidBufAmpSlice", ft); makeSCWrapper<NRTThreadedAmpSliceClient>("FluidBufAmpSlice", ft);
} }

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,7 +1,15 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) #include <clients/nrt/BufComposeClient.hpp>
#include <clients/nrt/BufferComposeNRT.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;
@ -10,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{ {
ft = inTable; ft = inTable;
using namespace fluid::client; using namespace fluid::client;
makeSCWrapper<NRTThreadedBufferCompose>("FluidBufCompose", ft); makeSCWrapper<NRTThreadedBufComposeClient>("FluidBufCompose", ft);
} }

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,8 +1,16 @@
// FD_BufHPSS, an NRT buffer HPSS Processor /*
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/HPSSClient.hpp> #include <clients/rt/HPSSClient.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;
@ -11,5 +19,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{ {
ft = inTable; ft = inTable;
using namespace fluid::client; using namespace fluid::client;
makeSCWrapper<NRTThreadedHPSS>("FluidBufHPSS", ft); makeSCWrapper<NRTThreadedHPSSClient>("FluidBufHPSS", ft);
} }

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,7 +1,15 @@
/*
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/LoudnessClient.hpp> #include <clients/rt/LoudnessClient.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,7 +1,15 @@
/*
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/MFCCClient.hpp> #include <clients/rt/MFCCClient.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,7 +1,15 @@
/*
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/MelBandsClient.hpp> #include <clients/rt/MelBandsClient.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,5 +1,15 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/nrt/NMFClient.hpp> #include <clients/nrt/NMFClient.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;
@ -8,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{ {
ft = inTable; ft = inTable;
using namespace fluid::client; using namespace fluid::client;
makeSCWrapper<NRTThreadedNMF>("FluidBufNMF", ft); makeSCWrapper<NRTThreadedNMFClient>("FluidBufNMF", ft);
} }

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,8 +1,15 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) #include <clients/rt/NoveltySliceClient.hpp>
#include <clients/rt/NoveltySlice.hpp>
#include <clients/nrt/FluidNRTClientWrapper.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;
@ -10,5 +17,5 @@ static InterfaceTable *ft;
PluginLoad(OfflineFluidDecompositionUGens) { PluginLoad(OfflineFluidDecompositionUGens) {
ft = inTable; ft = inTable;
using namespace fluid::client; using namespace fluid::client;
makeSCWrapper<NRTThreadingNoveltySlice>("FluidBufNoveltySlice", ft); makeSCWrapper<NRTThreadingNoveltySliceClient>("FluidBufNoveltySlice", ft);
} }

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,8 +1,15 @@
/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) #include <clients/rt/OnsetSliceClient.hpp>
#include <clients/rt/OnsetSlice.hpp>
#include <clients/nrt/FluidNRTClientWrapper.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;
@ -10,5 +17,5 @@ static InterfaceTable *ft;
PluginLoad(OfflineFluidDecompositionUGens) { PluginLoad(OfflineFluidDecompositionUGens) {
ft = inTable; ft = inTable;
using namespace fluid::client; using namespace fluid::client;
makeSCWrapper<NRTThreadingOnsetSlice>("FluidBufOnsetSlice", ft); makeSCWrapper<NRTThreadingOnsetSliceClient>("FluidBufOnsetSlice", ft);
} }

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,5 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,7 +1,15 @@
/*
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/PitchClient.hpp> #include <clients/rt/PitchClient.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,8 +1,15 @@
// FD_BufSines, an NRT buffer Sinusoidal Modelling Processor /*
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/SinesClient.hpp> #include <clients/rt/SinesClient.hpp>
#include <clients/nrt/FluidNRTClientWrapper.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;
@ -11,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{ {
ft = inTable; ft = inTable;
using namespace fluid::client; using namespace fluid::client;
makeSCWrapper<NRTThreadedSines>("FluidBufSines", ft); makeSCWrapper<NRTThreadedSinesClient>("FluidBufSines", ft);
} }

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -1,8 +1,15 @@
/*
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899) Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Unions Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/
#include <clients/rt/SpectralShapeClient.hpp> #include <clients/rt/SpectralShapeClient.hpp>
#include <clients/nrt/FluidNRTClientWrapper.hpp>
#include <FluidSCWrapper.hpp> #include <FluidSCWrapper.hpp>
static InterfaceTable *ft; static InterfaceTable *ft;

@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.3) # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
# Copyright 2017-2019 University of Huddersfield.
# Licensed under the BSD-3 License.
# See license.md file in the project root for full license information.
# This project has received funding from the European Research Council (ERC)
# under the European Unions Horizon 2020 research and innovation programme
# (grant agreement No 725899).
cmake_minimum_required(VERSION 3.11)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}") message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp) set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${FILENAME} ${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) include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save