HISE Logo Forum
    • Categories
    • Register
    • Login

    Global Cable + Third Party Node

    Scheduled Pinned Locked Moved Scripting
    3 Posts 2 Posters 76 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.
    • iamlampreyI
      iamlamprey
      last edited by

      Got a bit of a weird one. I'm trying to import a pitch-detection algorithm that works in JUCE, but I can't get it to send the data to a global cable.

      process() and setParameter() update it just fine

      prepare() and timerCallback() (JUCE override), only change the cable value when I add the node (scriptnode) or load the binary (hardcoded), then never again.

      // ==================================| Third Party Node Template |==================================
      
      #pragma once
      #include <JuceHeader.h>
      
      namespace project
      {
      using namespace juce;
      using namespace hise;
      using namespace scriptnode;
      using cable_manager_t = routing::global_cable_cpp_manager<SN_GLOBAL_CABLE(106677056)>;
      
      enum class GlobalCables
      {
      	pitch = 0
      };
      
      // ==========================| The node class with all required callbacks |==========================
      
      template <int NV> struct tuner: public data::base, public cable_manager_t, private juce::Timer
      {
      	// Metadata Definitions ------------------------------------------------------------------------
      	
      	SNEX_NODE(tuner);    
      
          tuner()
          {
              startTimerHz(30); // nope
          }
      
          ~tuner()
          {
              stopTimer(); // nope
          }
      	
      	struct MetadataClass
      	{
      		SN_NODE_ID("tuner");
      	};
      	
      	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;
      
      	int handleModulation(double& value) { return 0; }
      	template <typename T> void processFrame(T& data) {}
          void handleHiseEvent(HiseEvent& e) {}
          void setExternalData(const ExternalData& data, int index) {}
      	
      	// Scriptnode Callbacks ------------------------------------------------------------------------
          
      	void prepare(PrepareSpecs specs) 
      	{		
              randomGenerator.setSeedRandomly();
              float randomValue = randomGenerator.nextFloat();
              setGlobalCableValue<GlobalCables::pitch>(randomValue); // doesn't work here
              
              startTimerHz(30); // either doesn't start the timer, or it does, but the timer callback doesn't update the cable
      	}
      
      	void reset() 
          {
              stopTimer();
          }
      	
      	
      		
      	template <typename T> void process(T& data)
      	{		        
              randomGenerator.setSeedRandomly();
              float randomValue = randomGenerator.nextFloat();
              setGlobalCableValue<GlobalCables::pitch>(randomValue); // works (too many calls per second)
      	}	
      
          juce::Random randomGenerator;
      
      	void timerCallback() override
          {
              randomGenerator.setSeedRandomly();
              float randomValue = randomGenerator.nextFloat();
              setGlobalCableValue<GlobalCables::pitch>(randomValue); // nope    
          } 		    
      
      	// Parameter Functions -------------------------------------------------------------------------
      	
      	template <int P> void setParameter(double v)
      	{
      		if (P == 0)
      		{            
                  if (v >= 0.5) { monitorOutput.store(true); }
                  else { monitorOutput.store(false); }    
      		}
      		
      	}
      	
      	void createParameters(ParameterDataList& data)
      	{		
              {
                  parameter::data monitor("Monitor", { 0.0, 1.0 });
                  registerCallback<0>(monitor);
                  monitor.setDefaultValue(0.0);            
                  data.add(std::move(monitor));
              }        
      	}
      
      private:
      
      	std::atomic<bool> monitorOutput{ false };
      	
      };
      }
      
      Christoph HartC 1 Reply Last reply Reply Quote 0
      • Christoph HartC
        Christoph Hart @iamlamprey
        last edited by

        @iamlamprey ah yes actually that is a non-trivial issue: the juce::Timer (or juce::AsyncUpdater) class require the message thread to run and be available and that is not the case in the DLL build, because the code in there runs in a different process and cannot access the UI thread of the main application.

        I didn't notice that because I was always running a separate thread for communication, but I guess we need a better solution.

        So I've added a small helper class that runs a thread inside the DLL process and basically replicates the functionality of the juce::Timer and juce::AsyncUpdater class. All you need to do is to subclass from these classes instead of the JUCE ones and it will take care of everything:

        template <int NV> my_node: public juce::Timer // nope
        {
        
        };
        
        template <int NV> my_node: public hise::DllTimer // this will work
        {
            my_node()
            {
                startTimer(30);
            }
        
            void timerCallback() override { ... }
        };
        
        template <int NV> my_node: public juce::AsyncUpdater // nope
        {
        
        };
        
        template <int NV> my_node: public hise::DllAsyncUpdater // this will work
        {
            my_node()
            {
                
            }
        
            void process(ProcessData<2>& data) {
                triggerAsyncUpdate();
            }
        
            void handleAsyncUpdate() override {}
        };
        

        Once you compile the plugin, it will automatically resort to using the default JUCE classes with the proper UI thread model.

        iamlampreyI 1 Reply Last reply Reply Quote 4
        • iamlampreyI
          iamlamprey @Christoph Hart
          last edited by

          @Christoph-Hart Nice, thanks for the quick fix!

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

          17

          Online

          2.0k

          Users

          12.6k

          Topics

          109.8k

          Posts