diff --git a/include/algorithms/public/NMFMorph.hpp b/include/algorithms/public/NMFMorph.hpp index ccc23c14b..c55d84315 100644 --- a/include/algorithms/public/NMFMorph.hpp +++ b/include/algorithms/public/NMFMorph.hpp @@ -93,6 +93,21 @@ class NMFMorph mPos = (mPos + 1) % mH.cols(); } + void processFrame(ComplexVectorView v, RealVectorView hFrame, double interpolation) { + using namespace Eigen; + using namespace _impl; + MatrixXd W = MatrixXd::Zero(mW1.rows(), mW1.cols()); + for (int i = 0; i < W.cols(); i++) { + ArrayXd out = ArrayXd::Zero(mW2.rows()); + mOT[i].interpolate(interpolation, out); + W.col(i) = out; + } + + VectorXd frame = W * asEigen(hFrame); + RealVectorView mag1 = asFluid(frame); + mRTPGHI.processFrame(mag1, v, mWindowSize, mFFTSize, mHopSize, 1e-6); + } + private: MatrixXd mW1; MatrixXd mW2; diff --git a/include/clients/rt/NMFMorphInClient.hpp b/include/clients/rt/NMFMorphInClient.hpp new file mode 100644 index 000000000..c3e6abc9b --- /dev/null +++ b/include/clients/rt/NMFMorphInClient.hpp @@ -0,0 +1,133 @@ +/* +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 Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ +#pragma once + +#include "../../algorithms/public/NMFMorph.hpp" +#include "../../algorithms/public/NMF.hpp" +#include "clients/common/BufferedProcess.hpp" +#include "clients/common/FluidBaseClient.hpp" +#include "clients/common/ParameterConstraints.hpp" +#include "clients/common/ParameterSet.hpp" +#include "clients/common/ParameterTrackChanges.hpp" +#include "clients/common/ParameterTypes.hpp" + +namespace fluid { +namespace client { +namespace nmfmorph { + +enum NMFFilterIndex { + kSourceBuf, + kTargetBuf, + kAutoAssign, + kInterp, + kFFT, + kMaxFFTSize +}; + +constexpr auto NMFMorphParams = defineParameters( + InputBufferParam("source", "Source Bases"), + InputBufferParam("target", "Target Bases"), + EnumParam("autoassign", "Automatic assign", 1, "No", "Yes"), + FloatParam("interp", "Interpolation", 0, Min(0.0), Max(1.0)), + FFTParam("fftSettings", "FFT Settings", 1024, -1, -1), + LongParam>("maxFFTSize", "Maxiumm FFT Size", 16384, Min(4), + PowerOfTwo{})); + +class NMFMorphInClient : public FluidBaseClient, public AudioOut { + +public: + using ParamDescType = decltype(NMFMorphParams); + + using ParamSetViewType = ParameterSetView; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto getParameterDescriptors() { return NMFMorphParams; } + + NMFMorphInClient(ParamSetViewType &p) + : mParams{p}, mSTFTProcessor{get(), 1, 1} { + audioChannelsIn(1); + audioChannelsOut(1); + } + + index latency() { return get().winSize(); } + + void reset() { mSTFTProcessor.reset(); } + + template + void process(std::vector> &input, + std::vector> &output, FluidContext &c) { + assert(audioChannelsOut() && "No control channels"); + assert(output.size() >= asUnsigned(audioChannelsOut()) && + "Too few output channels"); + if (get().get() && get().get()) { + + auto &fftParams = get(); + auto sourceBuffer = BufferAdaptor::ReadAccess(get().get()); + auto targetBuffer = BufferAdaptor::ReadAccess(get().get()); + if (!sourceBuffer.valid() || !targetBuffer.valid()) { + return; + } + + index rank = sourceBuffer.numChans(); + if (targetBuffer.numChans() != rank) + return; + if (sourceBuffer.numFrames() != fftParams.frameSize()) { + return; + } + if (sourceBuffer.numFrames() != targetBuffer.numFrames()) { + return; + } + if (mTrackValues.changed(rank, fftParams.frameSize(),get())) { + tmpSource.resize(rank, fftParams.frameSize()); + tmpTarget.resize(rank, fftParams.frameSize()); + tmpAct.resize(rank); + tmpActMatrix.resize(rank, 1); + tmpMagnitude.resize(1, fftParams.frameSize()); + for (index i = 0; i < rank; ++i) { + tmpSource.row(i) = sourceBuffer.samps(i); + tmpTarget.row(i) = targetBuffer.samps(i); + } + mNMFMorph.init(tmpSource, tmpTarget, tmpActMatrix, fftParams.winSize(), + fftParams.fftSize(), fftParams.hopSize(), get() == 1); + } + + mSTFTProcessor.process(mParams, input, output, c, [&](ComplexMatrixView in, ComplexMatrixView out) { + algorithm::STFT::magnitude(in, tmpMagnitude); + mNMF.processFrame(tmpMagnitude.row(0), tmpSource, tmpAct); + mNMFMorph.processFrame(out.row(0), tmpAct, get()); + }); + } + } + +private: + ParameterTrackChanges mTrackValues; + STFTBufferedProcess mSTFTProcessor; + algorithm::NMFMorph mNMFMorph; + algorithm::NMF mNMF; + RealMatrix tmpSource; + RealMatrix tmpTarget; + RealMatrix tmpActMatrix; + RealMatrix tmpMagnitude; + RealVector tmpAct; +}; +} // namespace nmfmorph + +using RTNMFMorphInClient = ClientWrapper; + +} // namespace client +} // namespace fluid