I have ported CNMAT's [resonators~] object to Bela C++: https://github.com/jarmitage/resonators

It enables resonant filter bank synthesis and loading of resonance models.

Basic example:

#include <Bela.h>
#include "Resonators.h"
#include "Model.h"

ResonatorBank resBank;
ResonatorBankOptions resBankOptions = {};
ModelLoader model;

bool setup(BelaContext *context, void *userData) {
  model.load("models/marimba.json");
  resBankOptions.total = model.getSize();
  resBank.setup(resBankOptions, context->audioSampleRate, context->audioFrames);
  resBank.setBank(model.get());
  resBank.update();
  return true;
}

void render(BelaContext *context, void *userData) { 
  for (unsigned int n = 0; n < context->audioFrames; ++n) {
    float in = audioRead(context, n, 0); // an excitation signal
    float out = resBank.render(in);

    audioWrite(context, n, 0, out);
    audioWrite(context, n, 1, out);
  }
}

The corresponding marimba.json:

{
  "metadata": { 
    "name":        "marimba",
    "fundamental": 800,
    "resonators":  8
  },
  "resonators": [
    { "freq": 800,  "gain": 0.500000, "decay": 0.2 },
    { "freq": 1600, "gain": 0.033333, "decay": 0.4 },
    { "freq": 2400, "gain": 0.016666, "decay": 0.6 },
    { "freq": 3200, "gain": 0.006666, "decay": 0.7 },
    { "freq": 4000, "gain": 0.003333, "decay": 0.8 },
    { "freq": 4800, "gain": 0.001666, "decay": 0.9 },
    { "freq": 5400, "gain": 0.000666, "decay": 1.0 },
    { "freq": 6200, "gain": 0.000333, "decay": 1.0 }
  ]
}

The repo features:
- 5 Bela examples.
- ~100 resonance models (based on Ali Momeni's) in a JSON format.
- Python bindings for offline rendering / data science, and utility functions for working with models (still a bit messy).
- A work in progress p5.js interface for real-time GUI manipulation that works over Web Sockets.

It is designed for real-time manipulation of the model parameters, and has been trialled in a concert involving adding resonance to a pitch-tracked violin.

I am considering methods for generating models based on audio recordings, and methods for non-linear pitch envelope generation to "animate" the models. If anyone has any suggestions regarding those ideas I'd be grateful to hear them.

If you use this and find any problems or have feedback/suggestions, please let me know!

EDIT: Performance wise, I have been able to run ~120 resonators if I'm not updating in real-time, less if there's a lot of real-time updates happening. There hasn't been much thought put into optimisation yet, so suggestions welcome there too.

    jarm nice!

    was the violin pitch tracked on the Bela as well? do you have an example with midi control?

    • jarm replied to this.

      i guess the most obvious would be to "resonate" an external or internal synth, so instead of pitch-tracking just use the midi notes for pitch info.

      • jarm replied to this.

        you could try moving the definitions of the Resonator::render() and ResonatorBank::render() functions to Resonators.h (possibly inside the class declaration itself), and see if the compiler can achieve performance improvement by optimizing away some function calls.

        • jarm replied to this.

          lokki i guess the most obvious would be to "resonate" an external or internal synth, so instead of pitch-tracking just use the midi notes for pitch info.

          Yep, makes sense. This would be it basically:

          resBank.set(model.getShiftedToNote(midiNoteNumber));
          resBank.update();

          Created an issue for this: https://github.com/jarmitage/resonators/issues/3

          giuliomoro you could try moving the definitions of the Resonator::render() and ResonatorBank::render() functions to Resonators.h (possibly inside the class declaration itself), and see if the compiler can achieve performance improvement by optimizing away some function calls.

          Interesting, sounds like that's worth a try.

          How would that enable function calls to be optimised away?

          Issue here: https://github.com/jarmitage/resonators/issues/4

          The other idea I had which is a bit of a different approach, would be to try using the NEON vector math functions and filter example. I have no idea how much improvement one could expect from that.

            jarm How would that enable function calls to be optimised away?

            cpp files are compile one at a time (a "translation unit") into an object file. Object files are then linked together. When functions are compiled in separate object files, the final behaviour will be that calls from code in one file (e.g.: render.cpp) to another (e.g.: resonators.cpp) will have to be real function calls, and function calls come with overhead. Additionally, the compiler has no way of "seeing" what's inside the function, so it cannot, for instance, try to do clever optimisations when calling the same function over and over again.
            By placing the function(method) definition in the header, you give the compiler the chance to "see" what is inside the function and optimize accordingly. Possibly, it will optimize away the function call altogether, by inlining the code where the function os called (which could result in performance improvement, given how you are calling render() once per sample), possibly it will also do something even more clever.

            jarm The other idea I had which is a bit of a different approach, would be to try using the NEON vector math functions and filter example.

            That seems a good idea. Given how your Resonator::render ()seems to have recursive behaviour, it is probably not straightforward to optimize that one. However, you could re-write ResonatorBank::render() so that it performs vector operations on 4 resonators at a time. This could be a good exercise to learn NEON intrinsics. Note that whether this will provide benefits or not depends in whether the compiler is doing that already.
            A good starting point would be to add CPPFLAGs=-g -save-temps to your Make parameters and inspect the .s files that you'll find in ~/Bela after the build.

            Note: I know there are linking-time optimisations possible but I know nothing about those.

            3 years later

            This is great 🙂 Thank you

            I tried moving all the render specific functions .h file and it looked like it might give a small improvement...

            Btw. I did not compile you library -just used the cpp files 🙂 I have not used cmake much!
            Can you explain how to compile the whole lib such that the gui and all works ?

            Best

            I'm not sure what you mean by compile, you should be able to follow the Bela examples in the repo.

            The GUI was written before the Bela GUI was standardised so it's out of date, it might get updated at some point, but also maybe not 🙂