Struggling with learning plugin writing

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);
}
1 Like

If you are trying to learn C++ I cannot recommend Clion enough.

Its an IDE, but comes with all the tooling built in… sorting this out yourself is a pain and absolutely necessary, particularly for a beginner because C++ is such a silly language with many ways to ‘shoot yourself in the foot’ and the tools with all warnings enabled with highlighting just make this so much easier.

In my opinion, you need a bolts-and-kitchen-sink-included IDE to learn c++ , particularly if its your first language.

Clion has a price tag, but is free under many conditions Buy CLion: Pricing and Licensing, Discounts - JetBrains Toolbox Subscription, work on open source projects is one of them, so is being a student.

1 Like

Maybe I’m missing it, but are you writing anything into buf? Also, I don’t know that you need to NEXTPOWEROFTWO your buffer size - I think you can just allocate the size that you need.

Also, you can skip bit math tricks like & mask unless you’re using them because it helps the code read more clearly to you (it definitely doesn’t for me…). For example, if you’re trying to wrap an index to the length of your buffer, you can use std::remainderf or std::remainder.

@jordan thanks, i’ll check it out - i’ve been using nvim set up for c++ which was great so far (syntax highlighting and helpful error messages), but for some reason it really doesn’t get along with the cookiecutter plugin and even code from plugins which definitely work show errors that don’t really make sense. so maybe this will be more helpful!

@scztt i took a fair bit of that code from here because it was the simplest delay type plugin I could find. thanks for your feedback, i’ll see if i can get it working.

Oh well if you’ve already got nvim setup with clangd, clang-tidy and whatnot then I wouldn’t bothered. If you’re not getting proper lsp support then there is some odd flag you can pass to cmake to generate a JSON file that will fix that.