FFT Ugen C++ question

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:

PV_FreqWarp : PV_ChainUGen {
	*new { arg buffer;
		^this.multiNew('control', buffer)
	}
}

Any advice on this?

Best,
Stefano

1 Like

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?

Best,
Stefano

No. it doesn’t matter which CMake template you use, as long as it builds successfully :slight_smile:

You forgot to reassign the chain variable! As a consequence, the SynthDef might reorder PV_FreqWarp after IFFT and you would only hear the original sound.

You need to do either

chain = PV_FreqWarp(chain);
IFFT(chain).dup;

or

IFFT(PV_FreqWarp(chain)).dup;

Did you actually try this with other PV_* UGens?

You forgot to reassign the chain variable! As a consequence, the SynthDef might reorder PV_FreqWarp after IFFT 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.

Thanks! I’ll keep on developing the UGen.

Best,
Stefano

Hello again,
I think that a somewhat usable UGen is now complete and I’d like to share it here for comments, comparisons or improvements!

Please feel free to use it, modify it and recompile it!

Best,
Stefano

2 Likes