Crash when compiling dsp to dll
-
If I set the network to "allow compilation" in the "Record Demo" project, and then try to compile the network to dll, the compilation fails with this error message:
❌ /Users/ulrikboden/Music/Hise/HISE-develop/hi_dsp_library/node_api/nodes/processors.h:1055:3: static assertion failed due to requirement 'std::is_base_of<scriptnode::data::base, script_fx1_impl::simple_player<1>>() || std::is_base_of<scriptnode::data::base, script_fx1_impl::simple_player<1>>()': T must be base class of data::base
Is it supposed to work to use the compiled dll in a Hardcoded FX?
-
@ulrik Have you found a solution for this? I am currently getting the same error.
-
@Consint I have not tried it since the message, so no, sorry
-
@ulrik I got it to run by transferring my snex code into a third party c++ node.
-
@Consint could you share more about how you did this?
-
@HISEnberg said in Crash when compiling dsp to dll:
@Consint could you share more about how you did this?
I'm also interested in how you did that
-
@HISEnberg @ulrik Yes, but please be aware that I am only a amateur. I hope that people who are really familiar with it will intervene here if I have described something incorrectly or badly.
SNEX nodes and C++ nodes look very similar and work almost the same. If you create a C++ template under ‘Tools -> Create C++ third party node template’ and view the C++ file in an editor, you will see that the basic functions correspond to those of the SNEX node. I have essentially just copied the code from the SNEX node into the respective functions of the C++ node.
When doing this, you must pay attention to the following:
- The variables declared in the function header must be named as they are used (for example, sometimes data and sometimes just d)
- The conversion of the data to FrameData in the process function must happen as it is already in the C++ template (I don't know why).
Specific C++ node specifications must then be made. In the ‘Metadata Definitions’ at the top, static constexpr int NumAudioFiles = 1 must be set, as we want to play an audio file. In addition, the ‘Play’ knob must be created in the createParameters function at the bottom.
Once all this is done, the node can be compiled via ‘Export -> Compile DSP networks as dll’. After restarting Hise, the SNEX node in ‘Script FX1’ can be replaced by the C++ node. The entire Scriptnode can then be compiled.
Here is my C++ Node version of the SNEX simple_player:
// ==================================| Third Party Node Template |================================== #pragma once #include <JuceHeader.h> namespace project { using namespace juce; using namespace hise; using namespace scriptnode; // ==========================| The node class with all required callbacks |========================== template <int NV> struct s_player: public data::base { // Metadata Definitions ------------------------------------------------------------------------ SNEX_NODE(s_player); struct MetadataClass { SN_NODE_ID("s_player"); }; // set to true if you want this node to have a modulation dragger static constexpr bool isModNode() { return false; }; static constexpr bool isPolyphonic() { return NV > 1; }; // set to true if your node produces a tail static constexpr bool hasTail() { return false; }; // set to true if your doesn't generate sound from silence and can be suspended when the input signal is silent static constexpr bool isSuspendedOnSilence() { return false; }; // Undefine this method if you want a dynamic channel count static constexpr int getFixChannelAmount() { return 2; }; // Define the amount and types of external data slots you want to use static constexpr int NumTables = 0; static constexpr int NumSliderPacks = 0; static constexpr int NumAudioFiles = 1; static constexpr int NumFilters = 0; static constexpr int NumDisplayBuffers = 0; // Global Variables ------------------------------------------------------------------------- span<float, 8> dummy = { 0.0f }; ExternalData ed; // this is an array of two dynamic float buffers that will // hold the recorded data span<dyn<float>, 2> stereoData; int currentIndex = 0; int isPlaying = 0; // Scriptnode Callbacks ------------------------------------------------------------------------ void prepare(PrepareSpecs specs){} void reset(){} void handleHiseEvent(HiseEvent& e){} template <typename T> void processFrame(T& data) { // No interpolation necessary, just copy the // recorded buffer data[0] = stereoData[0][currentIndex]; data[1] = stereoData[1][currentIndex]; currentIndex++; if(currentIndex >= ed.numSamples) currentIndex = 0; } template <typename T> void process(T& data) { // We need to make sure that the audio render thread // isn't processing this function while the external data slot // is modified, so we use a DataReadLock with a try read access DataReadLock sl(ed, true); // check if the data lock was grabbed successfully, the playback // is enabled and the data slot is not empty if(sl.isLocked() && isPlaying && ed.numSamples > 0) { static constexpr int NumChannels = getFixChannelAmount(); // Cast the dynamic channel data to a fixed channel amount auto& fixData = data.template as<ProcessData<NumChannels>>(); auto fd = fixData.toFrameData(); while(fd.next()) processFrame(fd.toSpan()); // set the display position ed.setDisplayedValue((double)currentIndex); } } int handleModulation(double& value) { return 0; } void setExternalData(const ExternalData& d, int index) { ed = d; if(ed.numSamples > 0) { // refer the stereo data object to the audio buffer ed.referBlockTo(stereoData[0], 0); ed.referBlockTo(stereoData[1], Math.min(d.numChannels-1, 1)); } else { // route it to a dummy buffer if the slot is empty // (that's not required but otherwise the stereo data // might point to a dangling buffer) stereoData[0].referTo(dummy, 8, 0); stereoData[1].referTo(dummy, 8, 0); } } // Parameter Functions ------------------------------------------------------------------------- template <int P> void setParameter(double v) { auto shouldBePlaying = v > 0.5; if(isPlaying != shouldBePlaying) { isPlaying = shouldBePlaying; currentIndex = 0; } } void createParameters(ParameterDataList& data) { { // Create a parameter like this parameter::data p("Play", { 0.0, 1.0}); // The template parameter (<0>) will be forwarded to setParameter<P>() registerCallback<0>(p); p.setDefaultValue(0); data.add(std::move(p)); } } }; }
-
@Consint amazing I look forward to trying this out once I have access to my computer.
I wouldn't be to hard on yourself, I don't believe there are many users here too experienced with incorporating external C++ so errors are expected.
Thanks so much for sharing your process.
-
@Consint Thanks for the detailed explanation, it's much appreciated!