HISE Logo Forum
    • Categories
    • Register
    • Login

    How to turn an Airwindows effect into a Scriptnode

    Scheduled Pinned Locked Moved General Questions
    19 Posts 6 Posters 582 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • MorphoiceM
      Morphoice
      last edited by Morphoice

      It's been discussed a lot before, and It seems some people have managed to turn effects from the airwindows repository into nodes, so if anyone wants to share how they did it, I'd fancy an example.

      Christoph HartC C 2 Replies Last reply Reply Quote 0
      • Christoph HartC
        Christoph Hart @Morphoice
        last edited by

        @Morphoice There's this repo with some glue code but I haven't used it for quite a while:

        Link Preview Image
        GitHub - christoph-hart/vst2scriptnode: A simple boilerplate file to wrap headless VST 2.4 plugins into scriptnode nodes

        A simple boilerplate file to wrap headless VST 2.4 plugins into scriptnode nodes - christoph-hart/vst2scriptnode

        favicon

        GitHub (github.com)

        Let me know if it still works.

        C MorphoiceM 2 Replies Last reply Reply Quote 0
        • C
          Consint @Morphoice
          last edited by

          @Morphoice I found the solution some time ago in the code of a plugin from @oskarsh. You have to copy the .cpp and .h files of the plugin from Airwindows into the folder .../DspNetworks/ThirdParty/src (actually any path works but as far as I know this is the one Hise intended). Using Tube2 as an example, this is Tube2.cpp, Tube2.h and Tube2Proc.cpp.

          In the same directory you must also place the file AirWindows.h:

          #pragma once
          
          #include <math.h>
          #include <set>
          #include <string>
          
          #include <JuceHeader.h>
          
          #define __audioeffect__
          #define VstInt32             int32_t
          #define AudioEffect          ::airwindows::AirWindowsBase
          #define AudioEffectX         ::airwindows::AirWindowsBase
          #define audioMasterCallback  ::airwindows::SampleRateCallback*
          #define VstPlugCategory      int
          #define kPlugCategEffect     1
          #define kVstMaxProgNameLen   64
          #define kVstMaxParamStrLen   64
          #define kVstMaxProductStrLen 64
          #define kVstMaxVendorStrLen  64
          #define vst_strncpy          strncpy
          
          namespace airwindows {
          inline auto float2string(float f, char* text, int len) -> void
          {
              int decimals = 0;
              if (std::fabs(f) >= 10.0) {
                  decimals = 1;
              } else if (std::fabs(f) > 1.0) {
                  decimals = 2;
              } else {
                  decimals = 3;
              }
          
              juce::String str(f, decimals);
              str.copyToUTF8(text, (size_t)len);
          }
          
          inline auto int2string(float i, char* text, int len) -> void
          {
              juce::String str(i);
              str.copyToUTF8(text, (size_t)len);
          }
          
          inline auto dB2string(float value, char* text, int maxLen) -> void
          {
              if (value <= 0) {
                  vst_strncpy(text, "-oo", (size_t)maxLen);
              } else {
                  float2string((float)(20. * log10(value)), text, maxLen);
              }
          }
          
          struct SampleRateCallback
          {
              SampleRateCallback() = default;
          
              auto getSampleRate() const noexcept -> double { return sampleRate; }
          
              double sampleRate{0.0};
          };
          
          class AirWindowsBase
          {
          public:
              AirWindowsBase(SampleRateCallback* c, int prog, int param)
                  : numPrograms(prog)
                  , numParams(param)
                  , callback(c)
              {}
          
              int getNumInputs() { return numInputs; }
          
              int getNumOutputs() { return numOutputs; }
          
              int getNumParameters() { return numParams; }
          
              virtual bool getEffectName(char* name)                                                        = 0;
              virtual VstPlugCategory getPlugCategory()                                                     = 0;
              virtual bool getProductString(char* text)                                                     = 0;
              virtual bool getVendorString(char* text)                                                      = 0;
              virtual VstInt32 getVendorVersion()                                                           = 0;
              virtual void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames)         = 0;
              virtual void processDoubleReplacing(double** inputs, double** outputs, VstInt32 sampleFrames) = 0;
              virtual void getProgramName(char* name)                                                       = 0;
              virtual void setProgramName(char* name)                                                       = 0;
          
              virtual VstInt32 getChunk(void** data, bool isPreset)
              {
                  juce::ignoreUnused(data, isPreset);
                  return 0;
              };
          
              virtual VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset)
              {
                  juce::ignoreUnused(data, byteSize, isPreset);
                  return 0;
              };
          
              virtual float getParameter(VstInt32 index)
              {
                  juce::ignoreUnused(index);
                  return 0;
              }
          
              virtual void setParameter(VstInt32 index, float value) { juce::ignoreUnused(index, value); }
          
              virtual void getParameterLabel(VstInt32 index, char* text) { juce::ignoreUnused(index, text); }
          
              virtual void getParameterName(VstInt32 index, char* text) { juce::ignoreUnused(index, text); }
          
              virtual void getParameterDisplay(VstInt32 index, char* text) { juce::ignoreUnused(index, text); }
          
              virtual VstInt32 canDo(char* text) = 0;
          
          protected:
              void setNumInputs(int numIn) { numInputs = numIn; }
          
              void setNumOutputs(int numOut) { numOutputs = numOut; }
          
              void setUniqueID(int) {}
          
              void canProcessReplacing() {}
          
              void canDoubleReplacing() {}
          
              void programsAreChunks(bool) {}
          
              int numInputs = 0, numOutputs = 0, numPrograms = 0, numParams = 0;
          
              SampleRateCallback* callback;
          
              double getSampleRate() { return callback->getSampleRate(); }
          };
          
          }  // namespace airwindows
          
          #define DECLARE_AIRWINDOWS_NODE(ClassName, Namespace)                                                \
              template<int NV>                                                                                 \
              struct ClassName final : public data::base                                                       \
              {                                                                                                \
                  SNEX_NODE(ClassName);                                                                        \
                  struct MetadataClass                                                                         \
                  {                                                                                            \
                      SN_NODE_ID(#ClassName);                                                                  \
                  };                                                                                           \
                                                                                                               \
                  static constexpr bool isModNode() { return false; };                                         \
                  static constexpr bool isPolyphonic() { return NV > 1; };                                     \
                  static constexpr bool hasTail() { return false; };                                           \
                  static constexpr bool isSuspendedOnSilence() { return false; };                              \
                  static constexpr int getFixChannelAmount() { return 2; };                                    \
                  static constexpr int NumTables         = 0;                                                  \
                  static constexpr int NumSliderPacks    = 0;                                                  \
                  static constexpr int NumAudioFiles     = 0;                                                  \
                  static constexpr int NumFilters        = 0;                                                  \
                  static constexpr int NumDisplayBuffers = 0;                                                  \
                  void prepare(PrepareSpecs specs)                                                             \
                  {                                                                                            \
                      _sampleRateSource.sampleRate = specs.sampleRate;                                         \
                      _tmp.setSize(specs.numChannels, specs.blockSize);                                        \
                  }                                                                                            \
                  void reset() {}                                                                              \
                  void handleHiseEvent(HiseEvent& e) {}                                                        \
                  template<typename T>                                                                         \
                  void process(T& data)                                                                        \
                  {                                                                                            \
                      auto buffer = juce::AudioBuffer<float>(                                                  \
                          data.getRawChannelPointers(),                                                        \
                          data.getNumChannels(),                                                               \
                          data.getNumSamples()                                                                 \
                      );                                                                                       \
                      processInternal(buffer);                                                                 \
                  }                                                                                            \
                  template<typename T>                                                                         \
                  void processFrame(T& data)                                                                   \
                  {                                                                                            \
                      auto** ptr = (float**)alloca(data.size() * sizeof(float*));                              \
                      for (int i = 0; i < data.size(); i++) {                                                  \
                          ptr[i] = data.begin() + i;                                                           \
                      }                                                                                        \
                      auto buffer = juce::AudioBuffer<float>(ptr, data.size(), 1);                             \
                      processInternal(buffer);                                                                 \
                  }                                                                                            \
                  auto processInternal(juce::AudioBuffer<float>& buffer) -> void                               \
                  {                                                                                            \
                      _tmp.makeCopyOf(buffer);                                                                 \
                      _engine.processReplacing(                                                                \
                          _tmp.getArrayOfWritePointers(),                                                      \
                          buffer.getArrayOfWritePointers(),                                                    \
                          buffer.getNumSamples()                                                               \
                      );                                                                                       \
                  }                                                                                            \
                  int handleModulation(double& value) { return 0; }                                            \
                  void setExternalData(ExternalData const& data, int index) {}                                 \
                  template<int P>                                                                              \
                  void setParameter(double v)                                                                  \
                  {                                                                                            \
                      _engine.setParameter(P, static_cast<float>(v));                                          \
                  }                                                                                            \
                  void createParameters(ParameterDataList& data)                                               \
                  {                                                                                            \
                      for (auto i{0}; i < airwindows::Namespace::kNumParameters; ++i) {                        \
                          char name[kVstMaxParamStrLen]{};                                                     \
                          _engine.getParameterName(i, name);                                                   \
                          auto parameter = parameter::data(name, {0.0, 1.0});                                  \
                          registerCallback(parameter, i);                                                      \
                          parameter.setDefaultValue(_engine.getParameter(i));                                  \
                          data.add(std::move(parameter));                                                      \
                      }                                                                                        \
                  }                                                                                            \
                                                                                                               \
              private:                                                                                         \
                  template<typename Param>                                                                     \
                  auto registerCallback(Param& parameter, int i) -> void                                       \
                  {                                                                                            \
                      switch (i) {                                                                             \
                          case 0: registerCallback<0>(parameter); break;                                       \
                          case 1: registerCallback<1>(parameter); break;                                       \
                          case 2: registerCallback<2>(parameter); break;                                       \
                          case 3: registerCallback<3>(parameter); break;                                       \
                          case 4: registerCallback<4>(parameter); break;                                       \
                          case 5: registerCallback<5>(parameter); break;                                       \
                          case 6: registerCallback<6>(parameter); break;                                       \
                          case 7: registerCallback<7>(parameter); break;                                       \
                          case 8: registerCallback<8>(parameter); break;                                       \
                          case 9: registerCallback<9>(parameter); break;                                       \
                          case 10: registerCallback<10>(parameter); break;                                     \
                          case 11: registerCallback<11>(parameter); break;                                     \
                          case 12: registerCallback<12>(parameter); break;                                     \
                          case 13: registerCallback<13>(parameter); break;                                     \
                          case 14: registerCallback<14>(parameter); break;                                     \
                          case 15: registerCallback<15>(parameter); break;                                     \
                          case 16: registerCallback<16>(parameter); break;                                     \
                          case 17: registerCallback<17>(parameter); break;                                     \
                          case 18: registerCallback<18>(parameter); break;                                     \
                          case 19: registerCallback<19>(parameter); break;                                     \
                          case 20: registerCallback<20>(parameter); break;                                     \
                          case 21: registerCallback<21>(parameter); break;                                     \
                          case 22: registerCallback<22>(parameter); break;                                     \
                          case 23: registerCallback<23>(parameter); break;                                     \
                          case 24: registerCallback<24>(parameter); break;                                     \
                          default: break;                                                                      \
                      }                                                                                        \
                      jassert(false);                                                                          \
                  }                                                                                            \
                  airwindows::SampleRateCallback _sampleRateSource{};                                          \
                  airwindows::Namespace::ClassName _engine{&_sampleRateSource};                                \
                  juce::AudioBuffer<float> _tmp;                                                               \
              }
          

          You must then save the file Tube2.h in the directory .../DspNetworks/ThirdParty. In this file you have to set the paths to the files in the src folder and define the name, in this case Tube2, in several places. This is also the only file you need to edit if you want to include another Airwindows plugin:

          
          #pragma once
          
          #include <JuceHeader.h>
          
          #include "src/AirWindows.h"
          
          namespace airwindows::tube2_ns {
          JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE("-Wmultichar")
          #include "src/Tube2.h"
          
          #include "src/Tube2.cpp"
          #include "src/Tube2Proc.cpp"
          JUCE_END_IGNORE_WARNINGS_GCC_LIKE
          }  // namespace airwindows::tube2_ns
          
          namespace project {
          
          using namespace juce;
          using namespace hise;
          using namespace scriptnode;
          
          DECLARE_AIRWINDOWS_NODE(Tube2, tube2_ns);
          
          }  // namespace project
          

          And that's it. Then start Hise and press Compile DSP networks as DLL. This has worked for me for several (rather simple) Airwindows plugins.

          MorphoiceM 3 Replies Last reply Reply Quote 4
          • C
            Consint @Christoph Hart
            last edited by

            @Christoph-Hart Oh, great, I didn't know that existed.

            1 Reply Last reply Reply Quote 0
            • MorphoiceM
              Morphoice @Christoph Hart
              last edited by

              @Christoph-Hart isnt vst2 support officially dropped since cubase13?

              Christoph HartC 1 Reply Last reply Reply Quote 0
              • MorphoiceM
                Morphoice @Consint
                last edited by

                This post is deleted!
                1 Reply Last reply Reply Quote 0
                • Christoph HartC
                  Christoph Hart @Morphoice
                  last edited by

                  @Morphoice Yeah, VST2 as a plugin API is dead in the water but this just redirects the VST2 API calls to scriptnode so it's not actually using the VST2 SDK - it basically mimics the API so that you can reuse code that relies on the VST2 SDK.

                  how do I find it?

                  It should show up in the project.xxx node list. Have you created the C++ template first so that it registers the node file properly?

                  MorphoiceM 1 Reply Last reply Reply Quote 1
                  • MorphoiceM
                    Morphoice @Christoph Hart
                    last edited by

                    @Christoph-Hart got it as a hardcodedfx selection, works like a charm. however turning the value knobs introduces a lot of clicks and pops, very loudly... is there a way to smooth that?

                    oskarshO 1 Reply Last reply Reply Quote 0
                    • oskarshO
                      oskarsh @Morphoice
                      last edited by

                      @Morphoice unfortunately many airwindows nodes come with not real smoothing. You can try add a smoothed_parameter node.

                      HISE Developer for hire :)

                      1 Reply Last reply Reply Quote 0
                      • ChazroxC
                        Chazrox
                        last edited by

                        Man you guys got the cheat codes! 🙌 Can somebody do a walkthru of this process?

                        1 Reply Last reply Reply Quote 0
                        • MorphoiceM
                          Morphoice @Consint
                          last edited by

                          @Consint ever tried compiling on windows? It all works fine on mac but when I try to make a pc version I get tons of compiler errors upon compiling the DSP network. I have no idea what that means

                          !C:\HISE\HeatFilter\DspNetworks\Binaries\Source\DspNetwork.h(76,49): error C3203: 'Disintegrate': unspecialized class template can't be used as a template argument for template parameter 'Processors', expected a real type [C:\HISE\HeatFilter\DspNetworks\Binaries\Builds\VisualStudio2022\HeatFilter_DynamicLibrary.vcxproj]
                          
                          
                          C 1 Reply Last reply Reply Quote 0
                          • MorphoiceM
                            Morphoice @Consint
                            last edited by

                            This post is deleted!
                            1 Reply Last reply Reply Quote 0
                            • C
                              Consint @Morphoice
                              last edited by

                              @Morphoice I have only compiled it on Linux so far.

                              MorphoiceM 1 Reply Last reply Reply Quote 1
                              • MorphoiceM
                                Morphoice @Consint
                                last edited by

                                @Consint MSVC seems to be more strict about something in the Airwindows.h which CLANG on mac doesnt bother about.... I know nothing about template resolution so iI'm homping someone familiar with C++ can help...

                                griffinboyG C 2 Replies Last reply Reply Quote 0
                                • griffinboyG
                                  griffinboy @Morphoice
                                  last edited by griffinboy

                                  @Morphoice

                                  Have you tried asking chat gpt?
                                  If the library is small you can upload the c++ files and the error message and ask gpt to try and find what's wrong.

                                  It might fail to find the correct answer. But I find that 70% of issues it can solve. However there is a chance it will give you a bogus answer

                                  MorphoiceM 1 Reply Last reply Reply Quote 0
                                  • MorphoiceM
                                    Morphoice @griffinboy
                                    last edited by

                                    @griffinboy yeah but it only gives me unhelpful information I already know, the fact that MSVC is more strict about some template resolution. I don't know what that means and how to fix that, neither does GPT in a way that compiles. it just gives me code that produces new problems

                                    Christoph HartC 1 Reply Last reply Reply Quote 0
                                    • Christoph HartC
                                      Christoph Hart @Morphoice
                                      last edited by

                                      @Morphoice forget about all the noise - the issue is with your disintegrate network - the compiler thinks it‘s polyphonic but it isn‘t, hence it is missing the voice template parameter that causes the compiler error.

                                      Maybe it‘s best if you send me the disintegrate network as well as the one that is embedding it, might be a false positive with the code generator.

                                      MorphoiceM 1 Reply Last reply Reply Quote 2
                                      • C
                                        Consint @Morphoice
                                        last edited by

                                        @Morphoice I tried it with my plugin and was able to compile it in my Windows 10 VM with Visual Studio 2022 without any problems.

                                        1 Reply Last reply Reply Quote 0
                                        • MorphoiceM
                                          Morphoice @Christoph Hart
                                          last edited by

                                          @Christoph-Hart sent

                                          1 Reply Last reply Reply Quote 0
                                          • First post
                                            Last post

                                          22

                                          Online

                                          1.7k

                                          Users

                                          11.8k

                                          Topics

                                          102.4k

                                          Posts