Skip to content

Commit fed85fe

Browse files
committed
NMFMorphIn
Adds a new client to do NMF Morphing on live audio input
1 parent cdbe03c commit fed85fe

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

include/algorithms/public/NMFMorph.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@ class NMFMorph
9393
mPos = (mPos + 1) % mH.cols();
9494
}
9595

96+
void processFrame(ComplexVectorView v, RealVectorView hFrame, double interpolation) {
97+
using namespace Eigen;
98+
using namespace _impl;
99+
MatrixXd W = MatrixXd::Zero(mW1.rows(), mW1.cols());
100+
for (int i = 0; i < W.cols(); i++) {
101+
ArrayXd out = ArrayXd::Zero(mW2.rows());
102+
mOT[i].interpolate(interpolation, out);
103+
W.col(i) = out;
104+
}
105+
106+
VectorXd frame = W * asEigen<Matrix>(hFrame);
107+
RealVectorView mag1 = asFluid(frame);
108+
mRTPGHI.processFrame(mag1, v, mWindowSize, mFFTSize, mHopSize, 1e-6);
109+
}
110+
96111
private:
97112
MatrixXd mW1;
98113
MatrixXd mW2;
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
3+
Copyright 2017-2019 University of Huddersfield.
4+
Licensed under the BSD-3 License.
5+
See license.md file in the project root for full license information.
6+
This project has received funding from the European Research Council (ERC)
7+
under the European Union’s Horizon 2020 research and innovation programme
8+
(grant agreement No 725899).
9+
*/
10+
#pragma once
11+
12+
#include "../../algorithms/public/NMFMorph.hpp"
13+
#include "../../algorithms/public/NMF.hpp"
14+
#include "clients/common/BufferedProcess.hpp"
15+
#include "clients/common/FluidBaseClient.hpp"
16+
#include "clients/common/ParameterConstraints.hpp"
17+
#include "clients/common/ParameterSet.hpp"
18+
#include "clients/common/ParameterTrackChanges.hpp"
19+
#include "clients/common/ParameterTypes.hpp"
20+
21+
namespace fluid {
22+
namespace client {
23+
namespace nmfmorph {
24+
25+
enum NMFFilterIndex {
26+
kSourceBuf,
27+
kTargetBuf,
28+
kAutoAssign,
29+
kInterp,
30+
kFFT,
31+
kMaxFFTSize
32+
};
33+
34+
constexpr auto NMFMorphParams = defineParameters(
35+
InputBufferParam("source", "Source Bases"),
36+
InputBufferParam("target", "Target Bases"),
37+
EnumParam("autoassign", "Automatic assign", 1, "No", "Yes"),
38+
FloatParam("interp", "Interpolation", 0, Min(0.0), Max(1.0)),
39+
FFTParam<kMaxFFTSize>("fftSettings", "FFT Settings", 1024, -1, -1),
40+
LongParam<Fixed<true>>("maxFFTSize", "Maxiumm FFT Size", 16384, Min(4),
41+
PowerOfTwo{}));
42+
43+
class NMFMorphInClient : public FluidBaseClient, public AudioOut {
44+
45+
public:
46+
using ParamDescType = decltype(NMFMorphParams);
47+
48+
using ParamSetViewType = ParameterSetView<ParamDescType>;
49+
std::reference_wrapper<ParamSetViewType> mParams;
50+
51+
void setParams(ParamSetViewType& p) { mParams = p; }
52+
53+
template <size_t N>
54+
auto& get() const
55+
{
56+
return mParams.get().template get<N>();
57+
}
58+
59+
static constexpr auto getParameterDescriptors() { return NMFMorphParams; }
60+
61+
NMFMorphInClient(ParamSetViewType &p)
62+
: mParams{p}, mSTFTProcessor{get<kMaxFFTSize>(), 1, 1} {
63+
audioChannelsIn(1);
64+
audioChannelsOut(1);
65+
}
66+
67+
index latency() { return get<kFFT>().winSize(); }
68+
69+
void reset() { mSTFTProcessor.reset(); }
70+
71+
template <typename T>
72+
void process(std::vector<HostVector<T>> &input,
73+
std::vector<HostVector<T>> &output, FluidContext &c) {
74+
assert(audioChannelsOut() && "No control channels");
75+
assert(output.size() >= asUnsigned(audioChannelsOut()) &&
76+
"Too few output channels");
77+
if (get<kSourceBuf>().get() && get<kTargetBuf>().get()) {
78+
79+
auto &fftParams = get<kFFT>();
80+
auto sourceBuffer = BufferAdaptor::ReadAccess(get<kSourceBuf>().get());
81+
auto targetBuffer = BufferAdaptor::ReadAccess(get<kTargetBuf>().get());
82+
if (!sourceBuffer.valid() || !targetBuffer.valid()) {
83+
return;
84+
}
85+
86+
index rank = sourceBuffer.numChans();
87+
if (targetBuffer.numChans() != rank)
88+
return;
89+
if (sourceBuffer.numFrames() != fftParams.frameSize()) {
90+
return;
91+
}
92+
if (sourceBuffer.numFrames() != targetBuffer.numFrames()) {
93+
return;
94+
}
95+
if (mTrackValues.changed(rank, fftParams.frameSize(),get<kAutoAssign>())) {
96+
tmpSource.resize(rank, fftParams.frameSize());
97+
tmpTarget.resize(rank, fftParams.frameSize());
98+
tmpAct.resize(rank);
99+
tmpActMatrix.resize(rank, 1);
100+
tmpMagnitude.resize(1, fftParams.frameSize());
101+
for (index i = 0; i < rank; ++i) {
102+
tmpSource.row(i) = sourceBuffer.samps(i);
103+
tmpTarget.row(i) = targetBuffer.samps(i);
104+
}
105+
mNMFMorph.init(tmpSource, tmpTarget, tmpActMatrix, fftParams.winSize(),
106+
fftParams.fftSize(), fftParams.hopSize(), get<kAutoAssign>() == 1);
107+
}
108+
109+
mSTFTProcessor.process(mParams, input, output, c, [&](ComplexMatrixView in, ComplexMatrixView out) {
110+
algorithm::STFT::magnitude(in, tmpMagnitude);
111+
mNMF.processFrame(tmpMagnitude.row(0), tmpSource, tmpAct);
112+
mNMFMorph.processFrame(out.row(0), tmpAct, get<kInterp>());
113+
});
114+
}
115+
}
116+
117+
private:
118+
ParameterTrackChanges<index, index, index> mTrackValues;
119+
STFTBufferedProcess<ParamSetViewType, kFFT, true> mSTFTProcessor;
120+
algorithm::NMFMorph mNMFMorph;
121+
algorithm::NMF mNMF;
122+
RealMatrix tmpSource;
123+
RealMatrix tmpTarget;
124+
RealMatrix tmpActMatrix;
125+
RealMatrix tmpMagnitude;
126+
RealVector tmpAct;
127+
};
128+
} // namespace nmfmorph
129+
130+
using RTNMFMorphInClient = ClientWrapper<nmfmorph::NMFMorphInClient>;
131+
132+
} // namespace client
133+
} // namespace fluid

0 commit comments

Comments
 (0)