ohmtech-rdi / eurorack-blocks

Software to Hardware Prototyping for Eurorack using C++, Max/Gen~ or Faust
Other
315 stars 21 forks source link

Simulator crashes when loading faust module #601

Open niektb opened 1 year ago

niektb commented 1 year ago

With the default sample it doesn't happen but with my own projects it does... The preview in the library looks correct but the moment I try loading the module, VCVRack crashes instantly. This is the call stack from the debugger in Visual Studio Code:

ntdll.dll!ntdll!RtlIsZeroMemory (Unknown Source:0)
ntdll.dll!ntdll!.misaligned_access (Unknown Source:0)
ntdll.dll!ntdll!.misaligned_access (Unknown Source:0)
ntdll.dll!ntdll!.misaligned_access (Unknown Source:0)
ntdll.dll!ntdll!RtlGetCurrentServiceSessionId (Unknown Source:0)
ntdll.dll!ntdll!RtlFreeHeap (Unknown Source:0)
msvcrt.dll!msvcrt!free (Unknown Source:0)
plugin.dll!dsp_memory_manager::destroy(dsp_memory_manager * const this, void * ptr) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\FmSynth_erbb.hpp:65)
plugin.dll!deletemydspSIG0(mydspSIG0 * dsp, dsp_memory_manager * manager) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\module_faust.h:57)
plugin.dll!mydsp::classInit(int sample_rate) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\module_faust.h:128)
plugin.dll!FmSynth::init(FmSynth * const this) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\FmSynth_erbb.hpp:101)
plugin.dll!erb::module_init<FmSynth>(FmSynth & t) (c:\Users\niekt\eurorack-blocks\include\erb\module_init.h:41)
plugin.dll!ErbModule::ErbModule(ErbModule * const this) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\plugin_vcvrack.cpp:148)
plugin.dll!rack::createModel<ErbModule, ErbWidget>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)::TModel::createModule()(rack::TModel * const this) (c:\Users\niekt\eurorack-blocks\submodules\vcv-rack-sdk\include\helpers.hpp:27)
libRack.dll!libRack!_ZN4rack3app11AudioButton8onActionERKNS_6widget6Widget11ActionEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack3app7browser8ModelBox8onButtonERKNS_6widget6Widget11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6widget6Widget8onButtonERKNS1_11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6widget6Widget8onButtonERKNS1_11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6widget6Widget8onButtonERKNS1_11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack2ui12ScrollWidget8onButtonERKNS_6widget6Widget11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack3app7browser7Browser8onButtonERKNS_6widget6Widget11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack2ui11MenuOverlay8onButtonERKNS_6widget6Widget11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6widget12OpaqueWidget8onButtonERKNS0_6Widget11ButtonEventE (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6widget10EventState12handleButtonENS_4math3VecEiii (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6window3Svg4loadERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE (Unknown Source:0)
libRack.dll!libRack!glfwGetWin32Monitor (Unknown Source:0)
user32.dll!USER32!DispatchMessageW (Unknown Source:0)
user32.dll!USER32!CallWindowProcW (Unknown Source:0)
opengl32.dll!wglSwapBuffers (Unknown Source:0)
user32.dll!USER32!DispatchMessageW (Unknown Source:0)
user32.dll!USER32!DispatchMessageW (Unknown Source:0)
libRack.dll!libRack!_glfwPollEventsWin32 (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6window6Window4stepEv (Unknown Source:0)
libRack.dll!libRack!_ZN4rack6window6Window3runEv (Unknown Source:0)
[Unknown/Just-In-Time compiled code] (Unknown Source:0)
kernel32.dll!KERNEL32!BaseThreadInitThunk (Unknown Source:0)
ntdll.dll!ntdll!RtlUserThreadStart (Unknown Source:0)
[Unknown/Just-In-Time compiled code] (Unknown Source:0)

My faust code was tried and tested in the Faust IDE:

// FmSynth.dsp

import("stdfaust.lib");

freq = nentry("key",60,36,96,1) : midikey2hz 
with {
    // quarter tone tuning
    midikey2hz(mk) = 440.0*pow(2.0, (mk-69.0)/48.0); 
}; 

fm = hgroup("FM", os.osc(freq + os.osc(freq * ratio) * index))
with{
    ratio = hslider("Ratio[style:knob]", 2,1,10,1);
    index = hslider("Index[style:knob]", 100, 0, 1000, 0.01);
};

envelope = hgroup("Envelope", en.adsr(attack,decay,sustain, release, gate) * gain * 0.3)
with {
    attack = hslider("Envelope[style:knob]", 50,1,499,1)*0.001;
    decay = 0.1;
    sustain = 1;
    release = (500*0.001)-attack;
    gain = 1;
    gate = button("gate");
};

process = hgroup("Synth", fm * envelope <: _,_); 

My erbui file:

// FmSynth.erbui

module FmSynth {
   // The width of your module (don't make it smaller than back board width!)
   width 10hp

   // The back board to use
   board kivu12

   // The front panel material
   material aluminum
   // put "material aluminum black" if you want it black

   // Header to put your module name
   header { label "FmSynth" }

   // Put your controls here
   control pot1 Pot {
      faust { bind { address "/Synth/FM/Ratio" } }
      position 2.5hp, 18mm
      label "RATIO"
   }

   control pot2 Pot {
      faust { bind { address "/Synth/FM/Index" } }
      position 7.5hp, 18mm
      label "DEPTH"
   }

   control pot3 Pot {
      faust { bind { address "/Synth/Envelope/Envelope" } }
      position 2.5hp, 40mm
      label "ENV"
   }

   control gate_in GateIn {
      faust { bind { address "/Synth/Envelope/gate" } }
      position 2hp, 81mm
      label "GI1"
   }

   control cv_in CvIn {
      faust { bind { address "/Synth/FM/key" } }
      position 2hp, 96mm
      label "CV5"
   }

   control audio_out AudioOut {
      position 6hp, 111mm
      label "OUT L"
   }

   control audio_out2 AudioOut {
      position 8hp, 111mm
      label "OUT R"
   }

   // Available controls and styles are documented here:
   // https://eurorack-blocks.readthedocs.io/en/latest/controls/README.html

   // erbui grammar reference is here:
   // https://eurorack-blocks.readthedocs.io/en/latest/erbui/grammar.html
}
ohmtech-rdi commented 1 year ago

Hi @niektb and thanks for the report.

The following seems quite suspicious:

msvcrt.dll!msvcrt!free (Unknown Source:0)
plugin.dll!dsp_memory_manager::destroy(dsp_memory_manager * const this, void * ptr) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\FmSynth_erbb.hpp:65)
plugin.dll!deletemydspSIG0(mydspSIG0 * dsp, dsp_memory_manager * manager) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\module_faust.h:57)
plugin.dll!mydsp::classInit(int sample_rate) (c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\module_faust.h:128)

Which Faust version are you using? and could you please post the relevant Faust code that was generated? In particular the code around c:\Users\niekt\eurorack-blocks\Projects\fmsynth\artifacts\module_faust.h line 128.

Thanks!

niektb commented 1 year ago

Hi @ohmtech-rdi ! thank you for your swift response! Line 128 reads deletemydspSIG0(sig0, fManager);

Here is my full module_faust.h but github doesn't show line numbers:

/* ------------------------------------------------------------
name: "FmSynth"
Code generated with Faust 2.37.3 (https://faust.grame.fr)
Compilation options: -lang cpp -mem -es 1 -single -ftz 0
------------------------------------------------------------ */

#ifndef  __mydsp_H__
#define  __mydsp_H__

#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif 

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <math.h>

class mydspSIG0 {

  private:

    int iVec0[2];
    int iRec0[2];

  public:

    int getNumInputsmydspSIG0() {
        return 0;
    }
    int getNumOutputsmydspSIG0() {
        return 1;
    }

    void instanceInitmydspSIG0(int sample_rate) {
        for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
            iVec0[l0] = 0;
        }
        for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
            iRec0[l1] = 0;
        }
    }

    void fillmydspSIG0(int count, float* table) {
        for (int i1 = 0; (i1 < count); i1 = (i1 + 1)) {
            iVec0[0] = 1;
            iRec0[0] = ((iVec0[1] + iRec0[1]) % 65536);
            table[i1] = std::sin((9.58738019e-05f * float(iRec0[0])));
            iVec0[1] = iVec0[0];
            iRec0[1] = iRec0[0];
        }
    }

};

static mydspSIG0* newmydspSIG0(dsp_memory_manager* manager) { return (mydspSIG0*)new(manager->allocate(sizeof(mydspSIG0))) mydspSIG0(); }
static void deletemydspSIG0(mydspSIG0* dsp, dsp_memory_manager* manager) { dsp->~mydspSIG0(); manager->destroy(dsp); }

static float* ftbl0mydspSIG0 = 0;

#ifndef FAUSTCLASS 
#define FAUSTCLASS mydsp
#endif

#ifdef __APPLE__ 
#define exp10f __exp10f
#define exp10 __exp10
#endif

class mydsp : public dsp {

 private:

    int fSampleRate;
    float fConst0;
    float fConst1;
    FAUSTFLOAT fEntry0;
    FAUSTFLOAT fHslider0;
    float fConst2;
    FAUSTFLOAT fHslider1;
    float fRec2[2];
    float fRec1[2];
    float fConst3;
    FAUSTFLOAT fHslider2;
    FAUSTFLOAT fButton0;
    float fVec1[2];
    float fRec3[2];
    int iRec4[2];

 public:
    static dsp_memory_manager* fManager;

    void metadata(Meta* m) { 
        m->declare("basics.lib/name", "Faust Basic Element Library");
        m->declare("basics.lib/version", "0.1");
        m->declare("compile_options", "-lang cpp -mem -es 1 -single -ftz 0");
        m->declare("envelopes.lib/adsr:author", "Yann Orlarey and Andrey Bundin");
        m->declare("envelopes.lib/author", "GRAME");
        m->declare("envelopes.lib/copyright", "GRAME");
        m->declare("envelopes.lib/license", "LGPL with exception");
        m->declare("envelopes.lib/name", "Faust Envelope Library");
        m->declare("envelopes.lib/version", "0.1");
        m->declare("filename", "FmSynth.dsp");
        m->declare("maths.lib/author", "GRAME");
        m->declare("maths.lib/copyright", "GRAME");
        m->declare("maths.lib/license", "LGPL with exception");
        m->declare("maths.lib/name", "Faust Math Library");
        m->declare("maths.lib/version", "2.3");
        m->declare("name", "FmSynth");
        m->declare("oscillators.lib/name", "Faust Oscillator Library");
        m->declare("oscillators.lib/version", "0.1");
        m->declare("platform.lib/name", "Generic Platform Library");
        m->declare("platform.lib/version", "0.1");
    }

    virtual int getNumInputs() {
        return 0;
    }
    virtual int getNumOutputs() {
        return 2;
    }

    static void classInit(int sample_rate) {
        mydspSIG0* sig0 = newmydspSIG0(fManager);
        sig0->instanceInitmydspSIG0(sample_rate);
        ftbl0mydspSIG0 = static_cast<float*>(fManager->allocate(262144));
        sig0->fillmydspSIG0(65536, ftbl0mydspSIG0);
        deletemydspSIG0(sig0, fManager);
    }
    static void classDestroy() {
        fManager->destroy(ftbl0mydspSIG0);
    }

    virtual void instanceConstants(int sample_rate) {
        fSampleRate = sample_rate;
        fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
        fConst1 = (1.0f / fConst0);
        fConst2 = (440.0f / fConst0);
        fConst3 = (0.00100000005f * fConst0);
    }

    virtual void instanceResetUserInterface() {
        fEntry0 = FAUSTFLOAT(60.0f);
        fHslider0 = FAUSTFLOAT(100.0f);
        fHslider1 = FAUSTFLOAT(2.0f);
        fHslider2 = FAUSTFLOAT(50.0f);
        fButton0 = FAUSTFLOAT(0.0f);
    }

    virtual void instanceClear() {
        for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
            fRec2[l2] = 0.0f;
        }
        for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
            fRec1[l3] = 0.0f;
        }
        for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) {
            fVec1[l4] = 0.0f;
        }
        for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
            fRec3[l5] = 0.0f;
        }
        for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) {
            iRec4[l6] = 0;
        }
    }

    virtual void init(int sample_rate) {}
    virtual void instanceInit(int sample_rate) {
        instanceConstants(sample_rate);
        instanceResetUserInterface();
        instanceClear();
    }

    virtual mydsp* clone() {
        return new mydsp();
    }

    virtual int getSampleRate() {
        return fSampleRate;
    }

    virtual void buildUserInterface(UI* ui_interface) {
        ui_interface->openHorizontalBox("Synth");
        ui_interface->openHorizontalBox("Envelope");
        ui_interface->declare(&fHslider2, "style", "knob");
        ui_interface->addHorizontalSlider("Envelope", &fHslider2, FAUSTFLOAT(50.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(499.0f), FAUSTFLOAT(1.0f));
        ui_interface->declare(&fButton0, "CV", "2");
        ui_interface->addButton("gate", &fButton0);
        ui_interface->closeBox();
        ui_interface->openHorizontalBox("FM");
        ui_interface->declare(&fHslider0, "style", "knob");
        ui_interface->addHorizontalSlider("Index", &fHslider0, FAUSTFLOAT(100.0f), FAUSTFLOAT(0.0f), FAUSTFLOAT(1000.0f), FAUSTFLOAT(0.00999999978f));
        ui_interface->declare(&fHslider1, "style", "knob");
        ui_interface->addHorizontalSlider("Ratio", &fHslider1, FAUSTFLOAT(2.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(10.0f), FAUSTFLOAT(1.0f));
        ui_interface->declare(&fEntry0, "CV", "1");
        ui_interface->addNumEntry("key", &fEntry0, FAUSTFLOAT(60.0f), FAUSTFLOAT(36.0f), FAUSTFLOAT(96.0f), FAUSTFLOAT(1.0f));
        ui_interface->closeBox();
        ui_interface->closeBox();
    }

    virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
        FAUSTFLOAT* output0 = outputs[0];
        FAUSTFLOAT* output1 = outputs[1];
        float fSlow0 = std::pow(2.0f, (0.020833334f * (float(fEntry0) + -69.0f)));
        float fSlow1 = (440.0f * fSlow0);
        float fSlow2 = float(fHslider0);
        float fSlow3 = (fConst2 * (float(fHslider1) * fSlow0));
        float fSlow4 = float(fHslider2);
        float fSlow5 = (1.0f / std::max<float>(1.0f, (fConst3 * fSlow4)));
        float fSlow6 = float(fButton0);
        float fSlow7 = (1.0f / std::max<float>(1.0f, (fConst0 * (0.5f - (0.00100000005f * fSlow4)))));
        int iSlow8 = (fSlow6 == 0.0f);
        for (int i0 = 0; (i0 < count); i0 = (i0 + 1)) {
            fRec2[0] = (fSlow3 + (fRec2[1] - std::floor((fSlow3 + fRec2[1]))));
            float fTemp0 = (fRec1[1] + (fConst1 * (fSlow1 + (fSlow2 * ftbl0mydspSIG0[int((65536.0f * fRec2[0]))]))));
            fRec1[0] = (fTemp0 - std::floor(fTemp0));
            fVec1[0] = fSlow6;
            fRec3[0] = (fSlow6 + (fRec3[1] * float((fVec1[1] >= fSlow6))));
            iRec4[0] = (iSlow8 * (iRec4[1] + 1));
            float fTemp1 = (0.300000012f * (ftbl0mydspSIG0[int((65536.0f * fRec1[0]))] * std::max<float>(0.0f, (std::min<float>((fSlow5 * fRec3[0]), 1.0f) * (1.0f - (fSlow7 * float(iRec4[0])))))));
            output0[i0] = FAUSTFLOAT(fTemp1);
            output1[i0] = FAUSTFLOAT(fTemp1);
            fRec2[1] = fRec2[0];
            fRec1[1] = fRec1[0];
            fVec1[1] = fVec1[0];
            fRec3[1] = fRec3[0];
            iRec4[1] = iRec4[0];
        }
    }

};

dsp_memory_manager* mydsp::fManager = 0;

#endif
ohmtech-rdi commented 1 year ago

Hmm interesting, a little DSP is instantiated to fill a table and then it's gone. Didn't see this case before.

@sletz maybe we need to check this together, how do you handle this for FPGAs for example? That seems to be static data that could be either precomputed at build-time or filled once but in the case above mydspSIG0 could typically just live on the stack given its short lifetime (and all the troubles that deallocating brings on embedded platforms).

Or is it something latest Faust versions are doing differently?

sletz commented 1 year ago

Is the dsp_memory_manager* mydsp::fManager memory manager properly defined and created by the architecture file ?

niektb commented 1 year ago

Is that a question for me? where can i find that?

ohmtech-rdi commented 1 year ago

@niektb I think that's for me

@sletz I'm more concerned by this line:

static void deletemydspSIG0(mydspSIG0* dsp, dsp_memory_manager* manager) { dsp->~mydspSIG0(); manager->destroy(dsp); }

Since deallocating memory is not going to play so well on embedded platform with small memory (fragmentation, etc.)

Instead of allocating mydspSIG0 on the heap like in this portion of the code:

    static void classInit(int sample_rate) {
        mydspSIG0* sig0 = newmydspSIG0(fManager);
        sig0->instanceInitmydspSIG0(sample_rate);
        ftbl0mydspSIG0 = static_cast<float*>(fManager->allocate(262144));
        sig0->fillmydspSIG0(65536, ftbl0mydspSIG0);
        deletemydspSIG0(sig0, fManager);
    }

Why not use the stack for that, for example:

    static void classInit(int sample_rate) {
        mydspSIG0 sig0;
        sig0.instanceInitmydspSIG0(sample_rate);
        ftbl0mydspSIG0 = static_cast<float*>(fManager->allocate(262144));
        sig0.fillmydspSIG0(65536, ftbl0mydspSIG0);
    }
sletz commented 1 year ago

This is not trivial to express for now in our internal FIR (Faust Imperative Representation).

ohmtech-rdi commented 1 year ago

What is this SIG0? From what is it generated? @sletz

I'm trying to find a workaround:

sletz commented 1 year ago
sletz commented 1 year ago

Add -it option to inline rdtable/rwtable code in the main class in 2.65.0 https://github.com/grame-cncm/faust/commit/e14fa8642e5806df2a38f687aa416eb09ea5cfe0 @ohmtech-rdi can you test ?

ohmtech-rdi commented 1 year ago

Ah great, I'll try this sometimes soon and report, thanks! 👍