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.
    • 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

                        50

                        Online

                        1.7k

                        Users

                        11.7k

                        Topics

                        101.8k

                        Posts