HISE Logo Forum
    • Categories
    • Register
    • Login

    Any easy way to communicate from/to C++ and Javascript

    Scheduled Pinned Locked Moved C++ Development
    c++javascriptvaluetree
    11 Posts 3 Posters 1.6k 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.
    • jonhallurJ
      jonhallur
      last edited by

      I have a passable knowledge with JUCE having shipped one commercial product. I'm working with the ExternalFloatingTileTest tutorial from the hise_tutorial on github as my starting point as I need to have access to sysex send and receive. Thats all well and done, but now I need to pass information from the UI JS code to my C++ and vice versa.

      Does anybody have any hints. Does the UI code use ValueTree node change listeners (which I used as my main source of component message passing in JUCE) or are there some other clever Hise compoents I could inherit from where I could access ScriptComponents to set control callbacks when they change, or at least read/write ScriptComponent states.

      ( ps. I asked this on another thread I necroed the other day, but that has not gotten any replys)

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

        @jonhallur This depends a bit on the context - there are multiple ways to communicate with scripted controls.

        ValueTrees are used inside scriptnode as data model, but for the main HISE project architecture it's just used for saving and restoring.

        Although theoretically possible I would refrain from trying to attach any listeners to the script components themselves as there might be lifetime issues that might lead to subtle bugs. So I'd rather recommend a more black-box approach which gets the internals out of the way.

        If you just need to communicate float numbers, you can use the hise::Processor::setAttribute() function with your Interface script processor - they will be forwarded to the script component callbacks, but you need to know the index of your controls.

        If you want to have a global data storage that you need to access from both the script and your C++ code, you might be interested in hise::MainController::getGlobalVariableObject(), which returns a juce::DynamicObject that you can populate / query in C++ and access through the global keyword in HiseScript, but then you don't get any callbacks so you need to resort to polling.

        jonhallurJ 1 Reply Last reply Reply Quote 0
        • jonhallurJ
          jonhallur @Christoph Hart
          last edited by

          @christoph-hart Excellent, and thank you for taking the time to humor me.

          How do I get hise::Processor

          The only thing that had a setAttribute I could find was getMainController()->getMainSynthChain() but that only got me the synth module which I'm not trying to communicate with. Is there any object that would allow me to setAttribute on the ScriptButton, or some other UI component. Even just invoking the callback of the FloatingTile would get me to where I want to be.

          But the Globals object is pretty great. I can just create a lambda in C++

          _global->setMethod({ "sysEx" }, [](const var::NativeFunctionArgs& args) -> var {
          	DBG("Callback from UI code");
          	if (args.arguments->isArray()) {
          		DBG("Got an array back");
          	}
          	return var(0);
          });
          

          and invoke it from the JS code

          inline function onButton1Control(component, value)
          {
              if(value)
                  if(Globals.sysEx)
                      Globals.sysEx([5,4,3,2,1]);	
          };
          

          Which is almost everything I want to do.

          I could do the rest by having the UI timer pull the Globals object for changes, but I think that is the last solution I'd want to do.
          If I try to make a function on the Globals object on the JavaScripts side, it is not recognized as a method on the C++ side.
          I've tried functions, inline functions and anonymous functions, and C++ returns true on hasProperty() but always false on hasMethod() and invokeMethod() just fails quietly as there is a quiet little test that will just return an empty object when var decides that this is an object, but not a function.

          If you have some further insight I'd love to hear it as I am very close to where I would like to be, if this is just one of the limitations of the interop then I'll implement the timer polling of the Globals object.

          Thanks again for the help.

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

            Cool, didn't knew you could do this across C++/HiseScript boundaries (but watch out for data races, the scripting code is executed on its own thread which might interfere with your SysEx handling).

            If you have access to the MainController object, you can get the Interface processor pretty easy:

            // Fetch the first scriptprocessor that is called Interface (probably the one you need).
            raw::Reference<JavascriptMidiProcessor> ref(getMainController(), "Interface", false);
            
            // Don't make it crash if there isn't an interface
            if(auto p = ref.getProcessor())
                p->setAttribute(0, 0.6f, sendNotification);
            

            Ideally you'll cache the reference to the processor as the lookup is rather slow. Oh and

            BTW, the raw namespace contains a lot of functions that tuck away the internals of the full HISE codebase so I recommend trying to use it as much as possible. The docs are here:

            Link Preview Image
            HISE | Docs

            favicon

            (docs.hise.audio)

            (They weren't included in the last documentation builds).

            jonhallurJ 2 Replies Last reply Reply Quote 0
            • jonhallurJ
              jonhallur @Christoph Hart
              last edited by

              @christoph-hart Super, that solves everything I need.

              1 Reply Last reply Reply Quote 1
              • jonhallurJ
                jonhallur @Christoph Hart
                last edited by

                @christoph-hart I spoke a little too soon.

                1>d:\sources\hise\juce\modules\juce_core\memory\juce_weakreference.h(207): error C2385: ambiguous access of 'masterReference'
                1>d:\sources\hise\juce\modules\juce_core\memory\juce_weakreference.h(207): note: could be the 'masterReference' in base 'hise::ScriptBaseMidiProcessor'
                1>d:\sources\hise\juce\modules\juce_core\memory\juce_weakreference.h(207): note: or could be the 'masterReference' in base 'hise::JavascriptProcessor'
                1>d:\sources\hise\juce\modules\juce_core\memory\juce_weakreference.h(205): note: while compiling class template member function 'juce::ReferenceCountedObjectPtr<juce::WeakReference<ProcessorType,juce::ReferenceCountedObject>::SharedPointer> juce::WeakReference<ProcessorType,juce::ReferenceCountedObject>::getRef(ObjectType *)'
                ...
                1>d:\sources\hise\juce\modules\juce_core\memory\juce_weakreference.h(207): error C2248: 'hise::ScriptBaseMidiProcessor::masterReference': cannot access protected member declared in class 'hise::ScriptBaseMidiProcessor'
                
                Christoph HartC 1 Reply Last reply Reply Quote 0
                • Christoph HartC
                  Christoph Hart @jonhallur
                  last edited by

                  @jonhallur Should be fixed now.

                  lalalandsynthL jonhallurJ 2 Replies Last reply Reply Quote 1
                  • lalalandsynthL
                    lalalandsynth @Christoph Hart
                    last edited by

                    @christoph-hart Can this also be used to send an LFO as CC ?

                    https://lalalandaudio.com/

                    https://lalalandsynth.com/

                    https://www.facebook.com/lalalandsynth

                    https://www.facebook.com/lalalandsynth

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

                      @christoph-hart said in Any easy way to communicate from/to C++ and Javascript:

                      @jonhallur Should be fixed now.

                      Confirmed fixed in lasted pull from develop

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

                        @lalalandsynth yes if you want midi out I‘d recommend writing a custom c++ floating tile.

                        jonhallurJ 1 Reply Last reply Reply Quote 0
                        • jonhallurJ
                          jonhallur @Christoph Hart
                          last edited by

                          Just for future documentation and forum searches, this here code works in the other direction, that is calling into the Javascript from C++ from an inline function handed to C++, I image it would work for inline functions added to the Globals object as well.

                          _global->setMethod({ "set_callback" }, [&](const var::NativeFunctionArgs& args) -> var {
                              auto *obj = args.arguments->getDynamicObject();
                              if (obj) {
                                  if (auto p = _ref.getProcessor()) {
                                      juce::Result* res;
                                      var fVar(obj);
                                      var args[2] = { 42, 13 };
                                      LockHelpers::SafeLock sl(getMainController(), LockHelpers::ScriptLock);
                          
                                      p->getScriptEngine()->maximumExecutionTime = RelativeTime(3.0);
                                      p->getScriptEngine()->executeInlineFunction(fVar, args, res);
                                  }
                              }
                              return { 0 };
                          });
                          

                          Obviously this is not safe, and if you don't use an inline function, it will crash your C++ code.

                          This is rougly the code from

                          void ProcessorWithScriptingContent::customControlCallbackIdle(ScriptingApi::Content::ScriptComponent *component, const var& controllerValue, Result& r)
                          
                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post

                          46

                          Online

                          1.7k

                          Users

                          11.7k

                          Topics

                          101.8k

                          Posts