Hello,
following the discussion in this thread I am trying to make a specialised Ugen that implements frequency warping such as the GRM tool’s Warp. However, while the infrastructure of the Ugen itself works (I can load the plugin and it is recognised by SuperCollider), I cannot seem to change anything in the FFT process, making the Ugen itself only a passthrough.
Here’s the C++ code of a Ugen that should remove all magnitude from the FFT buffer:
#include "SC_fftlib.h"
#include "FFT_UGens.h"
#include "SCComplex.h"
#include "SC_PlugIn.hpp"
#include <vector>
#include <algorithm>
InterfaceTable *ft;
struct PV_FreqWarp : PV_Unit
{
SndBuf *m_buf;
};
void PV_FreqWarp_Ctor(PV_FreqWarp *unit);
void PV_FreqWarp_next(PV_FreqWarp *unit, int inNumSamples);
void PV_FreqWarp_Ctor(PV_FreqWarp *unit)
{
SETCALC(PV_FreqWarp_next);
ZOUT0(0) = ZIN0(0);
}
void PV_FreqWarp_next(PV_FreqWarp *unit, int inNumSamples)
{
PV_GET_BUF
SCPolarBuf *p = ToPolarApx(buf);
for (int i = 0; i < numbins; ++i) {
//should zero out all magnitudes
p->bin[i].mag = 0.;
}
}
void init_SCComplex(InterfaceTable *inTable);
#define DefinePVUnit(name) \
(*ft->fDefineUnit)(#name, sizeof(PV_Unit), (UnitCtorFunc)&name##_Ctor, 0, 0);
PluginLoad(PV_FreqWarp)
{
ft = inTable;
init_SCComplex(inTable);
DefinePVUnit(PV_FreqWarp);
}
However, this does not do anything in practice. Any advice on this? I thought that by accessing and writing in p->bin[i].mag and phase was the only thing needed (as stated by Dan Stowell in his chapter about this as well).
Here’s relative SuperCollider class:
You don’t need to call init_SCComplex anymore. If you look at SCComplex.h, you will see that the function is empty.
I guess you just copied this from PV_UGens.cpp? It’s not only unnecessary, it’s also wrong because you are telling the Server that your UGen is an instance of PV_Unit. Since the latter is smaller than PV_FreqWarp, the Server would allocate too little memory for your UGen!
Here’s the correct load function:
PluginLoad(PV_FreqWarp)
{
ft = inTable;
DefineSimpleUnit(PV_FreqWarp);
}
Side note: what’s the purpose of the SndBuf *m_buf; member? I don’t think you’re actually using it anywhere.
The remaining code looks fine, though. How did you test it?
Hi Spacechild,
thanks for the advice! I implemented your revisions.
I guess you just copied this from PV_UGens.cpp?
Yes, I am taking both PV_Ugens and JoshPV_Ugens as blueprints to make my own.
Side note: what’s the purpose of the SndBuf *m_buf; member? I don’t think you’re actually using it anywhere.
I have not used it, yet: sorry I included it within the example. Same with some of the unnecessary libraries which I am not sure I will be using 100%.
The remaining code looks fine, though. How did you test it?
Except from the class itself for PV_FreqWarp above, I have used this example to see whether the UGen would actually work:
(
var fftsize = 512;
z = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
x = {
|rate = 1|
var in, chain;
in = PlayBuf.ar(1, z, rate, loop: 1);
chain = FFT(LocalBuf(fftsize), in);
PV_FreqWarp(chain);
IFFT(chain).dup;
}.play;
)
… but unfortunately no effect in silencing the magnitudes. It seems such an easy task to modify bin values. I am compiling it with CMake following the guide here as it looks easier for me to follow than using the cookiecutter method. Might that be the problem?
No. it doesn’t matter which CMake template you use, as long as it builds successfully
You forgot to reassign the chain variable! As a consequence, the SynthDef might reorder PV_FreqWarpafterIFFT and you would only hear the original sound.
You forgot to reassign the chain variable! As a consequence, the SynthDef might reorder PV_FreqWarpafterIFFT and you would only hear the original sound.
And… that was it! I was too worried with the C++ code that I didn’t thoroughly check the SC part.