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
*.scx
.vs/
Darwin/*
Linux/*
Windows/*
install/

@ -1,22 +1,37 @@
####### original SC Cmake file starts here
cmake_minimum_required(VERSION 3.3)
# 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)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_EXTENSIONS OFF)
################################################################################
# Paths
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install" CACHE PATH "")
set(SC_LIBRARY_OUTPUT_PREFIX "release-packaging" CACHE STRING "Where in the hierarchy to write sc plugins (different for superbuild)")
set(SC_PATH "" CACHE PATH "Path to the top of SuperCollider source tree")
if(NOT SC_PATH)
message(FATAL_ERROR "SuperCollider source path is not set")
endif()
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")
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -mavx -msse -msse2 -msse3 -msse4")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")
SET(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7" CACHE STRING "Minimum OS X deployment version")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
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)
option(SUPERNOVA "Build plugins for supernova" OFF)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY ON)
MACRO(SUBDIRLIST result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
@ -29,62 +44,53 @@ MACRO(SUBDIRLIST result curdir)
SET(${result} ${dirlist})
ENDMACRO()
set(FLUID_PATH ~/fluid_decomposition CACHE PATH "The top level of the fluid_decomposition repo")
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")
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
get_filename_component(FLUID_ABS_PATH "${FLUID_PATH}" ABSOLUTE)
message("${FLUID_ABS_PATH}")
# if (NOT DEFINED ${FLUID_DECOMP_PATH})
# message(FATAL_ERROR "Please set the path to the fluid_decomposition sources with -DFLUID_DECOMP_PATH=<path>")
# endif()
FetchContent_Declare(
fluid_decomposition
GIT_REPOSITORY https://bitbucket.org/flucoma/fluid_decomposition.git
GIT_PROGRESS TRUE
)
if (NOT (EXISTS "${FLUID_ABS_PATH}/build/fluid_decomposition-exports.cmake"))
message(FATAL_ERROR "Can't find the fluid_decomposition CMake targets file at ${FLUID_ABS_PATH}/build/fluid_decomposition-exports.cmake. Please go to ${FLUID_ABS_PATH}/build and run CMake")
if(FLUID_PATH)
get_filename_component(
FETCHCONTENT_SOURCE_DIR_FLUID_DECOMPOSITION ${FLUID_PATH} ABSOLUTE
)
endif()
if (NOT (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/AudioFiles/"))
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging/AudioFiles/")
FetchContent_GetProperties(fluid_decomposition)
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()
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_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
set(CMAKE_SHARED_MODULE_PREFIX "")
if(APPLE OR WIN32)
set(CMAKE_SHARED_MODULE_SUFFIX ".scx")
endif()
get_property(FFT_SOURCES TARGET HISSTools_FFT PROPERTY INTERFACE_SOURCES)
get_property(FFT_LINK TARGET HISSTools_FFT PROPERTY INTERFACE_LINK_LIBRARIES)
add_library(FFTLIB STATIC ${FFT_SOURCES})
target_link_libraries(
FFTLIB PRIVATE ${FFT_LINK}
)
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
if(CMAKE_COMPILER_IS_GNUCXX)
target_compile_options( FFTLIB PUBLIC -fPIC )
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
target_compile_options( HISSTools_FFT PUBLIC -fPIC )
ENDIF()
add_library(FLUID_SC_WRAPPER INTERFACE)
target_include_directories(FLUID_SC_WRAPPER
INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include/"
)
target_sources(FLUID_SC_WRAPPER
INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include/FluidSCWrapper.hpp"
@ -98,3 +104,18 @@ foreach (project_dir ${PROJECT_DIRS})
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/${project_dir}")
endif ()
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:
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!
> 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
#include "SCBufferAdaptor.hpp"
@ -5,9 +15,8 @@
#include <clients/common/Result.hpp>
#include <data/FluidTensor.hpp>
#include <data/TensorTypes.hpp>
#include <FluidVersion.hpp>
#include <SC_PlugIn.hpp>
#include <algorithm>
#include <string>
#include <tuple>
@ -47,10 +56,7 @@ namespace impl {
// Iterate over kr/ir inputs via callbacks from params object
struct FloatControlsIter
{
FloatControlsIter(float **vals, size_t N)
: mValues(vals)
, mSize(N)
{}
FloatControlsIter(float** vals, index N) : mValues(vals), mSize(N) {}
float next() { return mCount >= mSize ? 0 : *mValues[mCount++]; }
@ -67,11 +73,11 @@ struct FloatControlsIter
}
private:
float** mValues;
size_t mSize;
size_t mCount{0};
index mSize;
index mCount{0};
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Real Time Processor
@ -81,8 +87,6 @@ class RealTime : public SCUnit
using HostVector = FluidTensorView<float, 1>;
using ParamSetType = typename Client::ParamSetType;
// using Client = typename Wrapper::ClientType;
public:
static void setup(InterfaceTable* ft, const char* name)
{
@ -92,45 +96,66 @@ public:
static void doLatency(Unit* unit, sc_msg_iter*)
{
float l[]{static_cast<float>(static_cast<Wrapper *>(unit)->mClient.latency())};
float l[]{
static_cast<float>(static_cast<Wrapper*>(unit)->mClient.latency())};
auto ft = Wrapper::getInterfaceTable();
std::stringstream ss;
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);
}
RealTime()
: mControlsIterator{mInBuf + mSpecialIndex + 1,static_cast<size_t>(static_cast<ptrdiff_t>(mNumInputs) - mSpecialIndex - 1)}
, mParams{Wrapper::Client::getParameterDescriptors()}
, mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)}
: mControlsIterator{mInBuf + mSpecialIndex + 1,
static_cast<index>(mNumInputs) - mSpecialIndex - 1},
mParams{Wrapper::Client::getParameterDescriptors()},
mClient{Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld,
mControlsIterator, true)}
{}
void init()
{
assert(!(mClient.audioChannelsOut() > 0 && mClient.controlChannelsOut() > 0) &&
assert(
!(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());
mInputConnections.reserve(mClient.audioChannelsIn());
mOutputConnections.reserve(mClient.audioChannelsOut());
mAudioInputs.reserve(mClient.audioChannelsIn());
mOutputs.reserve(std::max(mClient.audioChannelsOut(), mClient.controlChannelsOut()));
mInputConnections.reserve(asUnsigned(mClient.audioChannelsIn()));
mOutputConnections.reserve(asUnsigned(mClient.audioChannelsOut()));
mAudioInputs.reserve(asUnsigned(mClient.audioChannelsIn()));
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);
}
for (int i = 0; i < static_cast<int>(mClient.audioChannelsOut()); ++i)
for (index i = 0; i < mClient.audioChannelsOut(); ++i)
{
mOutputConnections.emplace_back(true);
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>();
Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1);
@ -139,18 +164,28 @@ public:
void next(int)
{
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();
const Unit* unit = this;
for (size_t i = 0; i < mClient.audioChannelsIn(); ++i)
for (index i = 0; i < mClient.audioChannelsIn(); ++i)
{
if (mInputConnections[i]) mAudioInputs[i].reset(IN(i), 0, fullBufferSize());
if (mInputConnections[asUnsigned(i)])
{ mAudioInputs[asUnsigned(i)].reset(IN(i), 0, fullBufferSize()); }
}
for (size_t i = 0; i < mClient.audioChannelsOut(); ++i)
for (index i = 0; i < mClient.audioChannelsOut(); ++i)
{
if (mOutputConnections[i]) mOutputs[i].reset(out(static_cast<int>(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 (index i = 0; i < mClient.controlChannelsOut(); ++i)
{
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);
}
@ -167,93 +202,103 @@ protected:
Client mClient;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// Non Real Time Processor
/// This is also a UGen, but the main action is delegated off to a worker 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
/// This is also a UGen, but the main action is delegated off to a worker
/// 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>
class NonRealTime : public SCUnit
{
using ParamSetType = typename Client::ParamSetType;
public:
static void setup(InterfaceTable* ft, const char* name)
{
registerUnit<Wrapper>(ft, name);
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);
w->mQueueEnabled = args->geti(0);
w->mFifoMsg.Set(w->mWorld,[](FifoMsg* f)
{
w->mFifoMsg.Set(
w->mWorld,
[](FifoMsg* f) {
auto w = static_cast<Wrapper*>(f->mData);
w->mClient.setQueueEnabled(w->mQueueEnabled);
},nullptr,w);
},
nullptr, w);
Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg);
});
ft->fDefineUnitCmd(name, "synchronous", [](struct Unit* unit, struct sc_msg_iter* args)
{
ft->fDefineUnitCmd(
name, "synchronous", [](struct Unit* unit, struct sc_msg_iter* args) {
auto w = static_cast<Wrapper*>(unit);
w->mSynchronous = args->geti(0);
w->mFifoMsg.Set(w->mWorld,[](FifoMsg* f)
{
w->mFifoMsg.Set(
w->mWorld,
[](FifoMsg* f) {
auto w = static_cast<Wrapper*>(f->mData);
w->mClient.setSynchronous(w->mSynchronous);
},nullptr,w);
},
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
NonRealTime() :
mControlsIterator{mInBuf,static_cast<size_t>(static_cast<ptrdiff_t>(mNumInputs) - mSpecialIndex - 2)}
, mParams{Wrapper::Client::getParameterDescriptors()}
, mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)}
, mSynchronous{mNumInputs > 2 ? (in0(static_cast<int>(mNumInputs - 1)) > 0) : false}
/// Penultimate input is the doneAction, final is blocking mode. Neither are
/// params, so we skip them in the controlsIterator
NonRealTime()
: mControlsIterator{mInBuf, index(mNumInputs) - mSpecialIndex - 2},
mParams{Wrapper::Client::getParameterDescriptors()},
mClient{Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld,
mControlsIterator, true)},
mSynchronous{mNumInputs > 2 ? (in0(int(mNumInputs) - 1) > 0) : false}
{}
~NonRealTime()
{
if (mClient.state() == ProcessState::kProcessing)
{
std::cout << Wrapper::getName() << ": Processing cancelled \n";
Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode,1,"/done",0,nullptr);
std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl;
Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode, 1, "/done",
0, nullptr);
}
// processing will be cancelled in ~NRTThreadAdaptor()
}
/// 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()
{
mFifoMsg.Set(mWorld, initNRTJob, nullptr, this);
mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg);
// 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>();
};
/// 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)
{
out0(0) = mDone ? 1.0 : static_cast<float>(mClient.progress());
out0(0) = mDone ? 1.0f : static_cast<float>(mClient.progress());
if (0 == pollCounter++ && !mCheckingForDone)
{
mCheckingForDone = true;
mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), this,
postProcess, exchangeBuffers, tidyUp, destroy,
0, nullptr);
mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(),
this, postProcess, exchangeBuffers,
tidyUp, destroy, 0, nullptr);
}
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)
{
auto w = static_cast<Wrapper*>(f->mData);
@ -264,7 +309,8 @@ public:
if (!result.ok())
{
std::cout << "ERROR: " << Wrapper::getName() << ": " << result.message().c_str() << std::endl;
std::cout << "ERROR: " << Wrapper::getName() << ": "
<< result.message().c_str() << std::endl;
return;
}
w->mClient.setSynchronous(w->mSynchronous);
@ -279,21 +325,29 @@ public:
Result r;
ProcessState s = w->mClient.checkProgress(r);
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)
{
//Given that cancellation from the language now always happens by freeing the
//synth, this block isn't reached normally. HOwever, if someone cancels using u_cmd, this is what will fire
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)
{
// Given that cancellation from the language now always happens by freeing
// the synth, this block isn't reached normally. HOwever, if someone
// 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;
return false;
}
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;
}
@ -304,17 +358,29 @@ public:
}
/// 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
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)
{
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)
{
int doneAction = static_cast<int>(w->in0(static_cast<int>(w->mNumInputs - 2))); //doneAction is penultimate input; THIS IS THE LAW
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(int(w->mNumInputs) -
2)); // doneAction is penultimate input; THIS IS THE LAW
world->ft->fDoneAction(doneAction, w);
return;
}
@ -325,8 +391,8 @@ public:
{
static_cast<Wrapper*>(unit)->mClient.cancel();
}
private:
private:
static Result validateParameters(NonRealTime* w)
{
auto results = w->mParams.constrainParameterValues();
@ -339,11 +405,14 @@ private:
bool exchangeBuffers(World* world) // RT thread
{
mParams.template forEachParamType<BufferT, impl::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)
//use replyID to convey status (0 = normal completion, 1 = cancelled)
if(mDone) world->ft->fSendNodeReply(&mParent->mNode,0,"/done",0,nullptr);
if(mCancelled) world->ft->fSendNodeReply(&mParent->mNode,1,"/done",0,nullptr);
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) use replyID to
// convey status (0 = normal completion, 1 = cancelled)
if (mDone)
world->ft->fSendNodeReply(&mParent->mNode, 0, "/done", 0, nullptr);
if (mCancelled)
world->ft->fSendNodeReply(&mParent->mNode, 1, "/done", 0, nullptr);
return true;
}
@ -353,15 +422,32 @@ private:
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;
size_t checkThreadInterval;
size_t pollCounter{0};
index checkThreadInterval;
index pollCounter{0};
protected:
ParamSetType mParams;
Client mClient;
@ -371,11 +457,12 @@ protected:
bool mCancelled{false};
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// An impossible monstrosty
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)
{
@ -384,7 +471,7 @@ class NonRealTimeAndRealTime : public RealTime<Client, Wrapper>, public NonRealT
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Template Specialisations for NRT/RT
@ -394,24 +481,24 @@ class FluidSCWrapperImpl;
template <typename Client, typename Wrapper>
class FluidSCWrapperImpl<Client, Wrapper, std::true_type, std::false_type>
: public NonRealTime<Client, Wrapper>
{
//public:
// FluidSCWrapperImpl(World* w, sc_msg_iter *args): NonRealTime<Client, Wrapper>(w,args){};
};
{};
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
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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// The main wrapper
template <typename C>
@ -434,7 +521,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
static auto fromArgs(World *w, FloatControlsIter& args, std::string, int)
{
//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));
if (!chunk) {
@ -442,7 +529,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
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[size] = 0; //terminate string
@ -475,8 +562,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
static auto fromArgs(World *w, ArgType args, InputBufferT::type&, int)
{
typename LongT::type bufnum = static_cast<LongT::type>(fromArgs(w, args, LongT::type(), -1));
return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr);
typename LongT::type bufnum =
static_cast<LongT::type>(fromArgs(w, args, LongT::type(), -1));
return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w)
: nullptr);
}
template<typename P>
@ -494,7 +583,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
template <typename ArgType, size_t N, typename T>
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)
{
@ -506,9 +595,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
}
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));
return a.value();
@ -715,14 +805,18 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase<C>
static void doVersion(Unit*, sc_msg_iter*)
{
std::cout << "Fluid Corpus Manipualtion Toolkit version " << fluidVersion()
<< std::endl;
}
public:
using Client = C;
using ParameterSetType = typename C::ParamSetType;
FluidSCWrapper()
{
impl::FluidSCWrapperBase<Client>::init();
}
FluidSCWrapper() { impl::FluidSCWrapperBase<Client>::init(); }
static const char* getName(const char* setName = nullptr)
{
@ -742,13 +836,26 @@ public:
getInterfaceTable(ft);
impl::FluidSCWrapperBase<Client>::setup(ft, name);
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)
{
// We won't even try and set params if the arguments don't match
if (inputs.size() == C::getParameterDescriptors().count())
{
p.template setParameterValues<ControlSetter>(verbose, world, inputs);
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 (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;
}

@ -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
#include <SC_PlugIn.h>
#include <SC_Errors.h>
#include <boost/align/aligned_alloc.hpp>
#include <cctype>
#include <data/FluidTensor.hpp>
#include <clients/common/BufferAdaptor.hpp>
#include <data/FluidTensor.hpp>
#include <SC_Errors.h>
#include <SC_PlugIn.h>
#include <cctype>
#include <fstream>
#include <iostream>
#include <sstream>
@ -13,22 +23,18 @@
#include <vector>
namespace fluid
{
namespace client
{
namespace fluid {
namespace client {
/**
A descendent of SndBuf that will populate itself
from the NRT mirror buffers given a world and a bufnum
**/
struct NRTBuf {
NRTBuf(SndBuf *b)
: mBuffer(b)
struct NRTBuf
{
}
NRTBuf(World *world, uint32 bufnum, bool rt = false)
: NRTBuf(rt ? World_GetBuf(world, bufnum)
: World_GetNRTBuf(world, bufnum))
NRTBuf(SndBuf* b) : mBuffer(b) {}
NRTBuf(World* world, index bufnum, bool rt = false)
: NRTBuf(rt ? World_GetBuf(world, static_cast<uint32>(bufnum))
: World_GetNRTBuf(world, static_cast<uint32>(bufnum)))
{
if (mBuffer && !static_cast<bool>(mBuffer->samplerate))
mBuffer->samplerate = world->mFullRate.mSampleRate;
@ -65,15 +71,9 @@ public:
SCBufferAdaptor& operator=(SCBufferAdaptor&&) = default;
SCBufferAdaptor(intptr_t bufnum,World *world, bool rt = false)
: NRTBuf(world, static_cast<uint32>(bufnum), rt)
, mBufnum(bufnum)
, mWorld(world)
{
}
SCBufferAdaptor() = default;
SCBufferAdaptor(index bufnum, World* world, bool rt = false)
: NRTBuf(world, bufnum, rt), mBufnum(bufnum), mWorld(world)
{}
~SCBufferAdaptor() { cleanUp(); }
@ -101,72 +101,70 @@ public:
// knows about
bool valid() const override
{
return (mBuffer && mBufnum >= 0 && mBufnum < mWorld->mNumSndBufs);
return (mBuffer && mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs));
}
bool exists() const override
{
return true;
}
bool exists() const override { return true; }
FluidTensorView<float, 1> samps(size_t channel) override
FluidTensorView<float, 1> samps(index channel) override
{
FluidTensorView<float, 2> v{mBuffer->data, 0,
static_cast<size_t>(mBuffer->frames),
static_cast<size_t>(mBuffer->channels)};
FluidTensorView<float, 2> v{mBuffer->data, 0, mBuffer->frames,
mBuffer->channels};
return v.col(channel);
}
FluidTensorView<float, 1> samps(size_t offset, size_t nframes,
size_t chanoffset) override
FluidTensorView<float, 1> samps(index offset, index nframes,
index chanoffset) override
{
FluidTensorView<float, 2> v{mBuffer->data, 0,
static_cast<size_t>(mBuffer->frames),
static_cast<size_t>(mBuffer->channels)};
FluidTensorView<float, 2> v{mBuffer->data, 0, mBuffer->frames,
mBuffer->channels};
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,
static_cast<size_t>(mBuffer->frames),
static_cast<size_t>(mBuffer->channels)};
FluidTensorView<const float, 2> v{mBuffer->data, 0, mBuffer->frames,
mBuffer->channels};
return v.col(channel);
}
FluidTensorView<const float, 1> samps(size_t offset, size_t nframes,
size_t chanoffset) const override
FluidTensorView<const float, 1> samps(index offset, index nframes,
index chanoffset) const override
{
FluidTensorView<const float, 2> v{mBuffer->data, 0,
static_cast<size_t>(mBuffer->frames),
static_cast<size_t>(mBuffer->channels)};
FluidTensorView<const float, 2> v{mBuffer->data, 0, mBuffer->frames,
mBuffer->channels};
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()); }
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;
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;
@ -178,14 +176,13 @@ public:
return r;
}
intptr_t bufnum() const { return mBufnum; }
index bufnum() const { return mBufnum; }
void realTime(bool rt) { mRealTime = rt; }
protected:
bool mRealTime{false};
float* mOldData{0};
intptr_t mBufnum;
index mBufnum;
World* mWorld;
};
@ -196,4 +193,3 @@ std::ostream& operator <<(std::ostream& os, SCBufferAdaptor& b)
} // namespace client
} // 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 {
*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;
^this.multiNew('audio', in.asAudioRateInput(this), absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0)
*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), fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq)
}
checkInputs {
if(inputs.at(16).rate != 'scalar') {
^(": maxSize cannot be modulated.");
};
^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 {
*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|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
*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|
source = source.asUGenInput;
indices = indices.asUGenInput;
@ -9,29 +8,29 @@ FluidBufAmpSlice : UGen {
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, 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(
server, this, action, [indices]
).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(
server, this, action, [indices], blocking: 1
).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 {
*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};
@ -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)
// 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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, windowSize, hopSize, fftSize, doneAction);
*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, 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(
server, this, action, [features]
).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(
server, this, action, [features], blocking:1
).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 {
*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;
destination = destination.asUGenInput;
resynth = resynth.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
destination = destination ? -1;
resynth = resynth ? -1;
bases = bases ? -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|
^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction);
*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, 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};
^FluidNRTProcess.new(
server, this, action, [destination, bases, activations].select{|x| x!= -1}
server, this, action, [resynth, bases, activations].select{|x| x!= -1}
).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};
^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(
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 {
*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};
@ -9,29 +9,29 @@ FluidBufNoveltySlice : UGen {
source.isNil.if {"FluidBufNoveltySlice: Invalid source 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(
server, this, action, [indices]
).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(
server, this, action, [indices], blocking:1
).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{
*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};
@ -13,26 +13,26 @@ FluidBufSines : UGen{
//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)
^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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, doneAction);
*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, 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(
server, this, action, [sines, residual].select{|x| x!= -1}
).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(
server, this, action, [sines, residual].select{|x| x!= -1}, blocking: 1
).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 {
*kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, maxNumBands = 120, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = 16384;
^this.multiNew('control', in.asAudioRateInput(this), numBands, minFreq, maxFreq, maxNumBands, windowSize, hopSize, fftSize, maxFFTSize);
*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, normalize, windowSize, hopSize, fftSize, maxFFTSize);
}
init {arg ...theInputs;

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

@ -1,6 +1,6 @@
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;
^this.multiNew('audio', in.asAudioRateInput(this), bandwidth, threshold, minTrackLen, magWeight, freqWeight ,windowSize, hopSize, fftSize, maxFFTSize)
*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, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize)
}
init { arg ... theInputs;
inputs = theInputs;
@ -11,7 +11,7 @@ FluidSines : MultiOutUGen {
^channels
}
checkInputs {
if(inputs.at(9).rate != 'scalar') {
if(inputs.at(13).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
^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
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');
// instantiate the listener to cue the processing from the language side
(
w = OSCFunc({ arg msg;
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);
)

@ -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
SUMMARY:: Amplitude-based Slicer
SUMMARY:: Amplitude-based Detrending Slicer
CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION::
This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute and relative amplitude changes as onsets and offsets. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European 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::
@ -18,166 +18,68 @@ METHOD:: ar
ARGUMENT:: in
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.
ARGUMENT:: absRampDown
ARGUMENT:: slowRampDown
The number of samples the absolute envelope follower will take to reach the next value when falling.
ARGUMENT:: absThreshOn
The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state.
ARGUMENT:: onThreshold
The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers.
ARGUMENT:: offThreshold
The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again.
ARGUMENT:: absThreshOff
The threshold in dB of the absolute envelope follower to trigger an offset, , aka to go ON when in OFF state.
ARGUMENT:: floor
The level in dB the slowRamp needs to be above to consider a detected difference valid, allowing to ignore the slices in the noise floor.
ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: minSilenceLength
The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored.
ARGUMENT:: minLengthAbove
The length in samples that the absolute envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: minLengthBelow
The length in samples that the absolute envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: lookBack
The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm.
ARGUMENT:: lookAhead
The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm.
ARGUMENT:: relRampUp
The number of samples the relative envelope follower will take to reach the next value when raising. Typically, this will be faster than absRampUp.
ARGUMENT:: relRampDown
The number of samples the relative envelope follower will take to reach the next value when falling. Typically, this will be faster than absRampDown.
ARGUMENT:: relThreshOn
The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers.
ARGUMENT:: relThreshOff
The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again.
ARGUMENT:: highPassFreq
The frequency of the fourth-order 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))::.
An audio stream with square envelopes around the slices. The latency between the input and the output is dependant on the relation between the two envelope followers.
EXAMPLES::
code::
//basic tests: absThresh sanity
(
{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
// 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: -16);
{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60);
[source, env]
}.plot(0.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);
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSliceLength:441);
{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60);
[source, env]
}.plot(0.1);
}.plot(0.08);
)
//basic tests: absThresh min silence
// another solution: minslicelength
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSilenceLength:441);
{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 220);
[source, env]
}.plot(0.1);
)
//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);
}.plot(0.08);
)
//mid tests: absThresh with lookBack
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookBack:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: absThresh with lookAhead
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookAhead:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: absThresh with asymetrical lookBack and lookAhead
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookBack:221, lookAhead:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//advanced tests: absThresh 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
//quick drum onsets
//load a buffer
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);
env = FluidAmpSlice.ar(source,absRampUp:44, absRampDown:2205, absThreshOn:-20, absThreshOff: -23, minSilenceLength:1100, lookBack:441);
[DelayN.ar(source,delaytime:441/44100), 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)]
env = FluidAmpSlice.ar(source,fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410,onThreshold: 10,offThreshold: 5,floor: -40,minSliceLength: 4410,highPassFreq: 20);
[source, env]
}.play;
)
::

@ -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
SUMMARY:: Amplitude-based Slicer for Buffers
SUMMARY:: Amplitude-based Detrending 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 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::
@ -40,48 +40,30 @@ ARGUMENT:: numChans
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
ARGUMENT:: 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.
ARGUMENT:: absRampDown
ARGUMENT:: slowRampDown
The number of samples the absolute envelope follower will take to reach the next value when falling.
ARGUMENT:: absThreshOn
The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state.
ARGUMENT:: onThreshold
The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers.
ARGUMENT:: offThreshold
The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again.
ARGUMENT:: absThreshOff
The threshold in dB of the absolute envelope follower to trigger an offset, , aka to go ON when in OFF state.
ARGUMENT:: floor
The level in dB the slowRamp needs to be above to consider a detected difference valid, allowing to ignore the slices in the noise floor.
ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: minSilenceLength
The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored.
ARGUMENT:: minLengthAbove
The length in samples that the absolute envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: minLengthBelow
The length in samples that the absolute envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: lookBack
The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm.
ARGUMENT:: lookAhead
The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm.
ARGUMENT:: relRampUp
The number of samples the relative envelope follower will take to reach the next value when raising. Typically, this will be faster than absRampUp.
ARGUMENT:: relRampDown
The number of samples the relative envelope follower will take to reach the next value when falling. Typically, this will be faster than absRampDown.
ARGUMENT:: relThreshOn
The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers.
ARGUMENT:: relThreshOff
The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again.
ARGUMENT:: highPassFreq
The frequency of the fourth-order 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::
code::
// detrending explained
// 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);
)
// the source is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track
b.play
b.plot
//basic tests: absThresh sanity
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12)
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)
//
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;})
c.getn(0,c.numFrames,{|item|item.postln;})
//solution: have to recut with relThresh
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-60, absThreshOff: -60, relRampUp:5, relRampDown:220, relThreshOn:2, relThreshOff:1)
//beware of multiple triggers at the begining of the 2nd cycle above). A solution: Schmidth triggers
FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60);
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
c.getn(0,c.numFrames,{|item|item.postln;})
//beware of double trig. a 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)
// we got most of them sorted, but there is another solution: minslicelength
FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 500);
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
c.getn(0,c.numFrames,{|item|item.postln;})
::
STRONG::A musical example.::
@ -172,24 +111,21 @@ c = Buffer.new(s);
)
// 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.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});})
c.getn(0,c.numFrames,{|item|item.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);
BufRd.kr(1, c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1),
BufRd.kr(1, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1),
BufRd.kr(1,c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1);
}.play;
)
::
@ -211,11 +147,11 @@ c = Buffer.new(s);
// with basic params
Routine{
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;
}.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.postln;})
// list the indicies of detected attacks - the two input channels have been summed.
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
DESCRIPTION::
A FluidBufCompose object provides a flexible utility for combining the contents of buffers on the server. It can be used for thing like mixing down multichannel buffers, or converting from left-right stereo to mid-side. It is used extensively in all the example code of LINK::Guides/FluidDecomposition:: as part of the FluCoMa project. footnote::
This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
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/
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);
d.query;
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 residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.::
It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::
This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European 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.
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/
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
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
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
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::
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::.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufMelBands
DESCRIPTION::
This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoeffs:: number of cepstral coefficients. It has the 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.
@ -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
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
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
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::
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.
@ -48,14 +48,17 @@ ARGUMENT:: minFreq
ARGUMENT:: maxFreq
The highest boundary of the highest band of the model, in Hz.
ARGUMENT:: normalize
This flag enables the scaling of the output to preserve the energy of the window. It is on (1) by default.
ARGUMENT:: windowSize
The window size. As spectral description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts.
The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision.
The 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
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.
More information on possible musicianly uses of NMF are availabe in LINK::Guides/FluCoMa:: overview file.
FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::
This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
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/
STRONG::Threading::
@ -59,7 +56,7 @@ ARGUMENT:: startChan
ARGUMENT:: numChans
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.
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
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
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
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)
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::
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
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;
f.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
(
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;
}.play;
)
@ -357,7 +354,7 @@ e.query
(
// use the seeding basis, without updating
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;
f.query;
g.query;
@ -376,7 +373,7 @@ g.plot;
(
// use the seeding bases, with updating this time
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;
f.query;
g.query;

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements a non-real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the 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.
@ -55,14 +55,17 @@ ARGUMENT:: threshold
ARGUMENT:: filterSize
The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes.
ARGUMENT:: minSliceLength
The minimum duration of a slice in number of hopSize.
ARGUMENT:: windowSize
The window size. As novelty estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts.
The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision.
The 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
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{
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;
}.play
)
@ -122,14 +125,14 @@ 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.
c.query;
//play slice number 2
//play slice number 3
(
{
BufRd.ar(1, b,
Line.ar(
BufRd.kr(1, c, DC.kr(2), 0, 1),
BufRd.kr(1, c, DC.kr(3), 0, 1),
(BufRd.kr(1, c, DC.kr(3)) - BufRd.kr(1, c, DC.kr(2), 0, 1) + 1) / s.sampleRate),
BufRd.kr(1, c, DC.kr(4), 0, 1),
(BufRd.kr(1, c, DC.kr(4)) - BufRd.kr(1, c, DC.kr(3), 0, 1) + 1) / s.sampleRate),
0,1);
}.play;
)
@ -167,5 +170,5 @@ Routine{
)
// 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
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.
@ -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).
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
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
Routine{
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;
}.play
)
// 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::
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.
@ -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
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
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
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::
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::
## a reconstruction of what it detects as sinusoidal;
## a residual derived from the previous buffer to allow null-summing::
The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. 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::
@ -47,28 +47,40 @@ ARGUMENT:: residual
The index of the buffer where the residual of the sinusoidal component will be reconstructed.
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
The normalised threshold, between 0 an 1, to consider a peak as a sinusoidal component from the normalized cross-correlation.
ARGUMENT:: detectionThreshold
The threshold in dB above which a magnitude peak is considered to be a sinusoidal component.
ARGUMENT:: birthLowThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the low end of the spectrum. It is interpolated across the spectrum until birthHighThreshold at half-Nyquist.
ARGUMENT:: birthHighThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the high end of the spectrum. It is interpolated across the spectrum until birthLowThreshold at DC.
ARGUMENT:: minTrackLen
The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove 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
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)
ARGUMENT:: trackFreqRange
The frequency difference allowed for a track to diverge between frames, in Hertz.
ARGUMENT:: freqWeight
The weight of the frequency proximity of a peak when trying to associate it to an existing track (relative to magWeight - suggested between 0 to 1)
ARGUMENT:: trackProb
The probability of the tracking algorithm to find a track.
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
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
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
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{
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;
}.play
)

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreadi
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:
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
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
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
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::
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::.

@ -4,7 +4,9 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements a simple tutorial object to illustrate and experiment with the behaviour of the FluidBuf* objects. It is part of the 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.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufTransients
DESCRIPTION::
This class implements a non-real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the 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.
@ -144,5 +144,5 @@ Routine{
)
// 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
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 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 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/
The algorithm will take a buffer in, and will divide it in two outputs: LIST::
## the transients, estimated from the signal and extracted from it;
## the remainder of the material, as estimated by the algorithm.::
The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. 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::

@ -5,7 +5,7 @@ RELATED:: Guides/FluCoMa, Guides/FluidDecomposition,Classes/UnaryOpFunction
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::

@ -14,11 +14,8 @@ The algorithm takes an audio in, and divides it into two or three outputs, depen
## a percussive component;
## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.::
It is part of the 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 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/
More information on median filtering, and on HPSS for musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file.
CLASSMETHODS::
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).
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
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
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/::.

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidMelBands
DESCRIPTION::
This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first 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.
@ -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).
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
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
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.
@ -28,6 +28,9 @@ ARGUMENT:: maxFreq
ARGUMENT:: maxNumBands
The maximum number of Mel bands that can be modelled. This sets the number of channels of the output, and therefore cannot be modulated.
ARGUMENT:: normalize
This flag enables the scaling of the output to preserve the energy of the window. It is on (1) by default.
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
@ -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).
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
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.
(
~winRange = 100;
~winRange = 0.1;
r = Routine {
{
b.get({ arg val;
@ -78,7 +81,7 @@ r = Routine {
(
x = {
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;
}.play;
)
@ -99,7 +102,7 @@ x = {arg bands = 40, low = 20, high = 20000;
)
//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
x.set(\bands,20)
@ -107,7 +110,7 @@ x.set(\bands,20)
// back to the full range
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)
// 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::
//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");
d = Group.new;
)
//play the source and s
//play the source and send the analysis on the
(
x = {
arg dry = 0.2;
@ -154,7 +157,7 @@ var stepMel = rangeMel / 41;
arg i;
var freqMel = (stepMel * (i +1)) + lowMel;
var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700;
{SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.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
(
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");
d = Group.new;
)
@ -193,11 +196,10 @@ x = {
var stepMel = rangeMel / 41;
var freqMel = (stepMel * (i +1)) + lowMel;
var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700;
SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.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
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.
More information on possible musicianly uses of NMF are availabe in LINK::Guides/FluCoMa:: overview file.
FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
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/
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).
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
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.
More information on possible musicianly uses of NMF are availabe in LINK::Guides/FluCoMa:: overview file.
FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
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/
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).
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
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::
CODE::
//load the source and declare buffers/arrays

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
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.
@ -34,6 +34,9 @@ ARGUMENT:: threshold
ARGUMENT:: filterSize
The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes.
ARGUMENT:: minSliceLength
The minimum duration of a slice in number of hopSize.
ARGUMENT:: windowSize
The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
@ -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).
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
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.
RETURNS::
An audio stream with impulses at detected transients. The latency between the input and the output is (windowSize +
((((kernelSize - 1) / 2) + (filterSize - 1)) * hopSize) at maximum.
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.
EXAMPLES::
@ -63,18 +65,18 @@ code::
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
{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
{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;
sig = PlayBuf.ar(1,b,loop:1);
trig = FluidNoveltySlice.ar(sig, 0, 11, 0.2, 4, 128);
syncd = DelayN.ar(sig, 1, ( (128 +((((11 - 1) / 2) + (4 - 1)) * 64)) / s.sampleRate));
trig = FluidNoveltySlice.ar(sig, 0, 11, 0.25, 5, 1, 128, 32);
syncd = DelayN.ar(sig, 1, (128 + (32 * (((11+1)/2) + (5 / 2))))/ s.sampleRate);
pan = TRand.ar(-1,1,trig);
Pan2.ar(syncd,pan);
}.play

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
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.
@ -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).
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
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
{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;

@ -4,7 +4,7 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Pitch
DESCRIPTION::
This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European 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.
@ -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).
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
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::
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::
@ -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).
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
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
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::
## a reconstruction of what it detects as sinusoidal;
## a residual derived from the previous signal to allow null-summing::
The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. 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::
@ -22,19 +22,31 @@ ARGUMENT:: in
The input to be processed
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
The normalised threshold, between 0 an 1, to consider a peak as a sinusoidal component from the normalized cross-correlation.
ARGUMENT:: detectionThreshold
The threshold in dB above which a magnitude peak is considered to be a sinusoidal component.
ARGUMENT:: birthLowThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the low end of the spectrum. It is interpolated across the spectrum until birthHighThreshold at half-Nyquist.
ARGUMENT:: birthHighThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the high end of the spectrum. It is interpolated across the spectrum until birthLowThreshold at DC.
ARGUMENT:: minTrackLen
The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove 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
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)
ARGUMENT:: trackMagRange
The amplitude difference allowed for a track to diverge between frames, in dB.
ARGUMENT:: freqWeight
The weight of the frequency proximity of a peak when trying to associate it to an existing track (relative to magWeight - suggested between 0 to 1)
ARGUMENT:: trackFreqRange
The frequency difference allowed for a track to diverge between frames, in Hertz.
ARGUMENT:: trackProb
The propensity of peaks to become enrolled into tracks.
ARGUMENT:: windowSize
The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
@ -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).
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
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");
// 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
{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
{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
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:
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).
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
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
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.
@ -59,7 +59,7 @@ b = Buffer.read(s,File.realpath(FluidTransientSlice.class.filenameSymbol).dirnam
// other parameters
{var sig = PlayBuf.ar(1,b,loop:1); [FluidTransientSlice.ar(sig,order:80,minSliceLength:2205) * 0.5, DelayN.ar(sig, 1, ((256 + 128 - 80)/ s.sampleRate))]}.play
// more musical trans-trigged autopan
// More musical, transient-trigged autopan
(
{
var sig, trig, syncd, pan;

@ -4,14 +4,13 @@ CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
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 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 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/
The algorithm will take an audio in, and will divide it in two outputs: LIST::
## the transients, estimated from the signal and extracted from it;
## the remainder of the material, as estimated by the algorithm. ::
The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. 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::

@ -4,8 +4,8 @@ categories:: Libraries>FluidDecomposition
related:: Classes/FluidBufNMF
description::
This is my description 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).::
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::
subsection:: stuff
This is my subsection
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).
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
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
f = {In.kr(d).poll}.play
@ -160,5 +160,3 @@ Routine{
}.play;
)
::

@ -10,6 +10,9 @@ subsection:: Slices
LINK:: Classes/FluidAmpSlice:: &
LINK:: Classes/FluidBufAmpSlice::
LINK:: Classes/FluidAmpGate:: &
LINK:: Classes/FluidBufAmpGate::
Slice by amplitude envelope
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)
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()
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()
set_target_properties(${PLUGIN} PROPERTIES
@ -13,16 +31,23 @@ set_target_properties(${PLUGIN} PROPERTIES
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(
${PLUGIN}
PUBLIC
PRIVATE
FLUID_DECOMPOSITION
# FLUID_MANIP
FLUID_SC_WRAPPER
PRIVATE
FFTLIB
HISSTools_FFT
)
target_include_directories(
${PLUGIN}
PRIVATE
@ -47,56 +72,32 @@ target_include_directories(
)
get_property(HEADERS TARGET FLUID_DECOMPOSITION PROPERTY INTERFACE_SOURCES)
source_group(TREE "${FLUID_PATH}/include" FILES ${HEADERS})
if (SUPERNOVA)
target_include_directories(
${PLUGIN}
SYSTEM PRIVATE
"${SC_PATH}/external_libraries/nova-tt"
"${SC_PATH}/external_libraries/boost_lockfree"
"${SC_PATH}/external_libraries/boost-lockfree"
)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG)
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()
source_group(TREE "${fluid_decomposition_SOURCE_DIR}/include" FILES ${HEADERS})
# get_property(HEADERS TARGET FLUID_MANIP PROPERTY INTERFACE_SOURCES)
# source_group(TREE "${fluid_manipulation_SOURCE_DIR}/include" FILES ${HEADERS})
#
# if (SUPERNOVA)
# target_include_directories(
# ${PLUGIN}
# SYSTEM PRIVATE
# "${SC_PATH}/external_libraries/nova-tt"
# "${SC_PATH}/external_libraries/boost_lockfree"
# "${SC_PATH}/external_libraries/boost-lockfree"
# )
# endif()
if(MINGW)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mstackrealign")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mstackrealign")
endif()
if(MSVC)
target_compile_options(${PLUGIN} PRIVATE /arch:AVX -D_USE_MATH_DEFINES)
else(MSVC)
target_compile_options(
${PLUGIN} PRIVATE $<$<NOT:$<CONFIG:DEBUG>>: -mavx -msse -msse2 -msse3 -msse4>
)
endif(MSVC)
####### added the fluid_decomposition
if(DEFINED FLUID_ARCH)
target_compile_options(${PLUGIN} PRIVATE ${FLUID_ARCH})
endif()
if(SUPERNOVA)
add_library(${PLUGIN}_supernova MODULE ${FILENAME})
set_property(TARGET ${PROJECT}_supernova
PROPERTY COMPILE_DEFINITIONS SUPERNOVA)
if(MSVC)
target_compile_options(${PLUGIN} PRIVATE -D_USE_MATH_DEFINES)
else()
target_compile_options(${PLUGIN} PRIVATE -fvisibility=hidden)
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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>
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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
// 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>
static InterfaceTable *ft;
@ -11,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{
ft = inTable;
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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>
static InterfaceTable *ft;
@ -10,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{
ft = inTable;
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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 <FluidSCWrapper.hpp>
static InterfaceTable *ft;
@ -11,5 +19,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{
ft = inTable;
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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 <FluidSCWrapper.hpp>
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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 <FluidSCWrapper.hpp>
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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 <FluidSCWrapper.hpp>
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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 <FluidSCWrapper.hpp>
static InterfaceTable *ft;
@ -8,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{
ft = inTable;
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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>
static InterfaceTable *ft;
@ -10,5 +17,5 @@ static InterfaceTable *ft;
PluginLoad(OfflineFluidDecompositionUGens) {
ft = inTable;
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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>
static InterfaceTable *ft;
@ -10,5 +17,5 @@ static InterfaceTable *ft;
PluginLoad(OfflineFluidDecompositionUGens) {
ft = inTable;
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,5 @@ add_library(
${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)

@ -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 <FluidSCWrapper.hpp>
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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/nrt/FluidNRTClientWrapper.hpp>
#include <FluidSCWrapper.hpp>
static InterfaceTable *ft;
@ -11,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens)
{
ft = inTable;
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

@ -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/nrt/FluidNRTClientWrapper.hpp>
#include <FluidSCWrapper.hpp>
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)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
@ -9,12 +18,4 @@ add_library(
${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)

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

Loading…
Cancel
Save