I’m trying to learn c++ and get to a point where I can write a few UGens - there are one or two things I’d like to implement myself, either as learning exercises or something particularly niche that isn’t in the class library. This is a big step up for me as I’ve previously only really worked with SC and played around with a few other languages on a very, very basic level. I’ve worked through the c++ official tutorial and was able to make a very standard WhiteNoise copy with help from the NOTAM Tutorial.
What I am struggling with is doing anything more advanced, eg. I set myself a goal of writing a feed-forward comb filter to learn how to deal with buffers, this has proved to be absolutely beyond me. I’ve checked out a few different plugins, most are much older than the NOTAM tutorial and the cookiecutter repo, and don’t seem to use the same style as suggested/generated there. I’ve also read through the Writing Unit Generators help file but after all this, I’m still unable to get my basic feed-forward filter working. I would be super grateful if anybody could a) point me in the right direction for helpful resources and b) offer a few pointers as to what I’m doing wrong here (it may well be many, many things…)
Cheers!
Jordan
// PluginFFComb.hpp
// Jordan White (jordanwhitede@gmail.com)
#pragma once
#include "SC_PlugIn.hpp"
namespace FFComb {
class FFComb : public SCUnit {
public:
FFComb();
// Destructor
~FFComb() {RTFree(buf); };
private:
// Calc function
void next(FFComb *unit, int nSamples);
// Member variables
float m_maxDelayTime; // in samples
int bufsize;
int mask;
float* buf;
int writephase;
};
} // namespace FFComb
// PluginFFComb.cpp
// Jordan White (jordanwhitede@gmail.com)
#include "SC_PlugIn.hpp"
#include "FFComb.hpp"
static InterfaceTable* ft;
// to do:
// 0. get args
// 1. declare and initialise buffer
// 2. write sig to buffer
// 3. read sig from buffer w delay
// 4. cubic interpolation??
// 5. mix signal and delaySig to output
namespace FFComb {
FFComb::FFComb() {
m_maxDelayTime = in(1);
bufsize = NEXTPOWEROFTWO((float*)SAMPLERATE * m_maxDelayTime);
mask = bufsize - 1;
// from writing ugens helpful, probably wrongly used
Unit* unit = (Unit*) this;
// allocate buffer
buf = (float*)RTAlloc(unit->mWorld, bufsize);
ClearUnitIfMemFailed(buf);
// initialise buffer
memset(unit->buf, 0, unit->bufsize * sizeof(float));
unit->writephase = 0;
mCalcFunc = make_calc_function<FFComb, &FFComb::next>();
next(1);
}
void FFComb::next(FFComb *unit, int nSamples) {
// input
float* input = in(0);
// delayTime
float delayTime = in(2);
// Control rate parameter: gain.
float gain = in(3);
// Output buffer
float* outbuf = out(0);
// cap delay at m_maxDelayTime
if (delayTime > m_maxDelayTime) {
delayTime = m_maxDelayTime;
}
// not sure what this actually means but it's probably important: Compute the delay in samples and the integer and fractional parts of this delay.
float delay_samples = (float)SAMPLERATE * m_maxDelayTime;
int offset = delay_samples;
float frac = delay_samples - offset;
// the actual function
for (int i = 0; i < nSamples; ++i) {
// four phases for cubic interpolation
int phase1 = writephase - offset;
int phase2 = phase1 - 1;
int phase3 = phase1 - 2;
int phase0 = phase1 + 1;
float d0 = buf[phase0 & mask];
float d1 = buf[phase1 & mask];
float d2 = buf[phase2 & mask];
float d3 = buf[phase3 & mask];
float delayed = cubicinterp(frac, d0, d1, d2, d3);
delayed = delayed * gain;
out[i] = zapgremlins(in[i] + delayed);
writephase = writephase + 1 & mask;
}
unit->writephase = writephase;
}
} // namespace FFComb
PluginLoad(FFCombUGens) {
// Plugin magic
ft = inTable;
registerUnit<FFComb::FFComb>(ft, "FFComb", false);
}