Create Interfase on HISE to be used on JUCE



  • Hi guys, doing some weird stuff here and need some guidance.

    It's there a way to create UI within HISE and then connect it to a custom DSP code on JUCE? I already have C++ code with UI elements declared (like opening the AutogeneratedProject.jucer file and being able to edit it)
    JUCE is a pain for UI design and HISE rocks on that!

    OR..... EVEN BETTER...

    It's there a way to add a C++ DSP code with declared sliders/buttons etc to HISE?

    I know it's a huge question. Any guidance will be appreciated

    P.S: I have a really nice DSP code made on Faust and I can export it to JUCE, or C++ and all I want is to create a nice UI for it on HISE



  • @hisefilo said in Create Interfase on HISE to be used on JUCE:

    It's there a way to add a C++ DSP code with declared sliders/buttons etc to HISE?

    I'm about to go down this road in a few... got some Matlab projects I wanna import into HISE. I love the combined scripting/gui IDE



  • Yes, there are multiple ways to combine HISE with custom C++.

    1. Add a custom UI element as floating tile
    2. Create the entire instrument in C++ by combining the HISE modules with a JUCE GUI. That's what I did in PercX and offers the greatest flexibility, but you need to write proper C++ code 🙂

    However both approaches are targeted at the UI side, replacing the HISE interface stuff with C++ for total customizability.

    Now if you just want to import DSP algorithms in C++ (either from FAUST or Matlab), the best way would be to use the scriptnode API, create a wrapper for the code around it and then compile HISE with the node. Then you can just connect your UI to the node parameters as you would to "native" HISE modules.

    Writing the glue code should be a very trivial task (after all all plugin APIs are more or less the same).



  • @Christoph-Hart said in Create Interfase on HISE to be used on JUCE:

    create a wrapper for the code around it and then compile HISE with the node

    LOL this sounds trivial for you!!! Not for me. Any example?????

    BTW you just made my next 4 month busy trying to make this work out!!! 🙂



  • @Christoph-Hart on 1: Can't ever see where the custom elements are placed on AutogeneratedProject.jucer
    Any clue??

    This is how a HISE project with 3 knobs and 1 floating title looks like

    Screen Shot 2020-05-16 at 14.38.17.png



  • Everything C++ code needs to go into the AdditionalSourceCode directory and will be copied automatically into the build folder (see here for an example).

    NEVER edit the files in the build folder, this folder is supposed to be a throw-away temporary folder (in fact it's good advice to add the entire Builds folder to the git ignore list.



  • @hisefilo creating ui in juce is a lot more free than in hise, once you get used to it. (Nothing against Christophs great work of course!). The paint routines are basically the same in juce and you have the benefit of having all the extra features juce has to offer right at your fingertips, the removeFrom method is hugely useful as is the flexi box system. Plus you get the benefit of a scalable ui without the fixed ratio.



  • @Christoph-Hart will be my homework then 😞 thanks mate



  • @LightandSound A bit hard for me since it's needed to compile every time to see any changes. But I know it's super powerful



  • @hisefilo said in Create Interfase on HISE to be used on JUCE:

    A bit hard for me since it's needed to compile every time to see any changes.

    Welcome to the slow world of "real" programming 😛

    Always build debug while testing, it's much faster.



  • @d-healey thanks!!! see you there 😛

    I'm really impressed with Faust simplicity. you can create a super complex DSP algorithm with just one line of code. If we are able to use HISE scripting and GUI power plus Faust...



  • @Christoph-Hart said in Create Interfase on HISE to be used on JUCE:

    create a wrapper for the code around it and then compile HISE with the node.

    Any clue on how to wrap this simple one oscillator C++ example into a node??? Or any node-wrapping tutorial/example?

    /* ------------------------------------------------------------
    name: "sine"
    Code generated with Faust 2.23.8 (https://faust.grame.fr)
    Compilation options: -lang cpp -scal -ftz 0
    ------------------------------------------------------------ */
    
    #ifndef  __mydsp_H__
    #define  __mydsp_H__
    
    #ifndef FAUSTFLOAT
    #define FAUSTFLOAT float
    #endif 
    
    #include <algorithm>
    #include <cmath>
    #include <math.h>
    
    class mydspSIG0 {
    	
      private:
    	
    	int iRec0[2];
    	
      public:
    	
    	int getNumInputsmydspSIG0() {
    		return 0;
    	}
    	int getNumOutputsmydspSIG0() {
    		return 1;
    	}
    	int getInputRatemydspSIG0(int channel) {
    		int rate;
    		switch ((channel)) {
    			default: {
    				rate = -1;
    				break;
    			}
    		}
    		return rate;
    	}
    	int getOutputRatemydspSIG0(int channel) {
    		int rate;
    		switch ((channel)) {
    			case 0: {
    				rate = 0;
    				break;
    			}
    			default: {
    				rate = -1;
    				break;
    			}
    		}
    		return rate;
    	}
    	
    	void instanceInitmydspSIG0(int sample_rate) {
    		for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
    			iRec0[l0] = 0;
    		}
    	}
    	
    	void fillmydspSIG0(int count, float* table) {
    		for (int i = 0; (i < count); i = (i + 1)) {
    			iRec0[0] = (iRec0[1] + 1);
    			table[i] = std::sin((9.58738019e-05f * float((iRec0[0] + -1))));
    			iRec0[1] = iRec0[0];
    		}
    	}
    
    };
    
    static mydspSIG0* newmydspSIG0() { return (mydspSIG0*)new mydspSIG0(); }
    static void deletemydspSIG0(mydspSIG0* dsp) { delete dsp; }
    
    static float ftbl0mydspSIG0[65536];
    
    #ifndef FAUSTCLASS 
    #define FAUSTCLASS mydsp
    #endif
    
    #ifdef __APPLE__ 
    #define exp10f __exp10f
    #define exp10 __exp10
    #endif
    
    class mydsp : public dsp {
    	
     private:
    	
    	int fSampleRate;
    	float fConst0;
    	float fRec1[2];
    	
     public:
    	
    	void metadata(Meta* m) { 
    		m->declare("filename", "sine.dsp");
    		m->declare("math.lib/author", "GRAME");
    		m->declare("math.lib/copyright", "GRAME");
    		m->declare("math.lib/deprecated", "This library is deprecated and is not maintained anymore. It will be removed in August 2017.");
    		m->declare("math.lib/license", "LGPL with exception");
    		m->declare("math.lib/name", "Math Library");
    		m->declare("math.lib/version", "1.0");
    		m->declare("music.lib/author", "GRAME");
    		m->declare("music.lib/copyright", "GRAME");
    		m->declare("music.lib/deprecated", "This library is deprecated and is not maintained anymore. It will be removed in August 2017.");
    		m->declare("music.lib/license", "LGPL with exception");
    		m->declare("music.lib/name", "Music Library");
    		m->declare("music.lib/version", "1.0");
    		m->declare("name", "sine");
    	}
    
    	virtual int getNumInputs() {
    		return 0;
    	}
    	virtual int getNumOutputs() {
    		return 1;
    	}
    	virtual int getInputRate(int channel) {
    		int rate;
    		switch ((channel)) {
    			default: {
    				rate = -1;
    				break;
    			}
    		}
    		return rate;
    	}
    	virtual int getOutputRate(int channel) {
    		int rate;
    		switch ((channel)) {
    			case 0: {
    				rate = 1;
    				break;
    			}
    			default: {
    				rate = -1;
    				break;
    			}
    		}
    		return rate;
    	}
    	
    	static void classInit(int sample_rate) {
    		mydspSIG0* sig0 = newmydspSIG0();
    		sig0->instanceInitmydspSIG0(sample_rate);
    		sig0->fillmydspSIG0(65536, ftbl0mydspSIG0);
    		deletemydspSIG0(sig0);
    	}
    	
    	virtual void instanceConstants(int sample_rate) {
    		fSampleRate = sample_rate;
    		fConst0 = (440.0f / std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))));
    	}
    	
    	virtual void instanceResetUserInterface() {
    	}
    	
    	virtual void instanceClear() {
    		for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
    			fRec1[l1] = 0.0f;
    		}
    	}
    	
    	virtual void init(int sample_rate) {
    		classInit(sample_rate);
    		instanceInit(sample_rate);
    	}
    	virtual void instanceInit(int sample_rate) {
    		instanceConstants(sample_rate);
    		instanceResetUserInterface();
    		instanceClear();
    	}
    	
    	virtual mydsp* clone() {
    		return new mydsp();
    	}
    	
    	virtual int getSampleRate() {
    		return fSampleRate;
    	}
    	
    	virtual void buildUserInterface(UI* ui_interface) {
    		ui_interface->openVerticalBox("sine");
    		ui_interface->closeBox();
    	}
    	
    	virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
    		FAUSTFLOAT* output0 = outputs[0];
    		for (int i = 0; (i < count); i = (i + 1)) {
    			fRec1[0] = (fConst0 + (fRec1[1] - std::floor((fConst0 + fRec1[1]))));
    			output0[i] = FAUSTFLOAT(ftbl0mydspSIG0[int((65536.0f * fRec1[0]))]);
    			fRec1[1] = fRec1[0];
    		}
    	}
    
    };
    
    #endif
    
    


  • @hisefilo A couple years back, Christoph suggested I look at his MDA effect wrapper for inspiration.
    check out

    HISE-scriptnode\hi_modules\effects
    


  • @dustbro thanks mate!! will take a look (C++ still being a pain for me)



  • Hmm, I think it's a little bit over your head to make a proper wrapper for that code (also I think I need to tinker with the compiler flags to see if I can change the code a little bit as there are a few annoying things in it). The most promising solution would be if I just create a wrapper node for faust code that you then can use.



  • @Christoph-Hart said in Create Interfase on HISE to be used on JUCE:

    The most promising solution would be if I just create a wrapper node for faust code that you then can use.

    That would be awesome and a way to many faust users to get into HISE (Faust lacks of GUI designer)

    JOS will be so happy if that happens 🙂
    This is the faust2juce script if it helps

    #! /bin/bash -e
    
    #####################################################################
    #                                                                   #
    #           Compiles Faust programs to JUCE standalone or plugin    #
    #           (c) Grame, 2016-2019                                    #
    #                                                                   #
    #####################################################################
    
    . faustpath
    . faustoptflags
    . usage.sh
    
    CXXFLAGS=$MYGCCFLAGS
    
    DEBUG=false
    
    #-------------------------------------------------------------------
    #PHASE 1 : dispatch command arguments
    #-------------------------------------------------------------------
    
    OSCLIB=""
    POLY="POLY"
    DEF=""
    EFFECT=""
    NVOICES=-1
    STANDALONE="0"
    IS_SYNTH="0"
    IS_MIDI="0"
    IS_LLVM="0"
    JUCE_POLY="0"
    FAUSTFLOAT="float"
    JUCER=""
    JUCE_MODULES_DIR="\.\.\/\.\.\/modules"  # By default, the generated folder is supposed to be copied in JUCE/examples folder
    
    while [ $1 ]
    do
        p=$1
    
        if [ $p = "--help" ] || [ $p = "-help" ] || [ $p = "-h" ]; then
            usage faust2juce "[options] [Faust options] <file.dsp>"
            require Juce 
            echo "Compiles Faust programs to JUCE standalone or plugin."
            option
            options -osc -midi -soundfile
            option "-nvoices <num>"
            option "-effect <effect.dsp>"
            option "-effect auto"
            option -standalone "to produce a standalone project, otherwise a plugin project is generated"
            option -"jucemodulesdir <folder>" "to set JUCE modules directory to <folder>, such as ~/JUCE/modules"
            option -jsynth "to use JUCE polyphonic Synthesizer instead of Faust polyphonic code"
            option -llvm "to use the LLVM compilation chain (OSX and Linux for now)"
            exit
        fi
    
        if [ "$p" = -debug ]; then
        	DEBUG=true
        elif [ $p = "-nvoices" ]; then
            shift
            NVOICES=$1
        elif [ $p = "-standalone" ]; then
            STANDALONE="1"
        elif [ $p = "-effect" ]; then
            DEF+="POLY2 "
            POLY="POLY2"
            shift
            EFFECT=$1
        elif [ $p = "-jucemodulesdir" ]; then
    	    shift
    	    JUCE_MODULES_DIR=$(echo $1 | sed -e 's/\//\\\//g')
        elif [ $p = "-jsynth" ]; then
            DEF+="JUCE_POLY "
            JUCE_POLY="1"
        elif [ $p = "-midi" ]; then
            DEF+="MIDICTRL "
            IS_MIDI="1"
        elif [ $p = "-osc" ]; then
        	DEF+="OSCCTRL "
    	elif [ $p = "-llvm" ]; then
    		IS_LLVM="1"
        elif [ $p = "-soundfile" ]; then
            DEF+="SOUNDFILE "
        elif [ ${p:0:1} = "-" ]; then
            if [ $p = "-double" ]; then
                FAUSTFLOAT="double"
            fi
    	    OPTIONS="$OPTIONS $p"
    	elif [[ -f "$p" ]]; then
    	    FILES="$FILES $p"
    	else
    	    OPTIONS="$OPTIONS $p"        
    	fi
    
    shift
    
    done
    
    # configure JUCER file
    
    if [ $STANDALONE = "1" ]; then
        if [ $IS_LLVM = "1" ]; then
            JUCER="standalone-llvm.jucer"
        else
            JUCER="standalone.jucer"
        fi
    else
        if [ $IS_LLVM = "1" ]; then
            JUCER="plugin-llvm.jucer"
        else
            JUCER="plugin.jucer"
        fi
    fi
    
    if [ $STANDALONE = "1" ] && [ $JUCE_POLY = "1" ]; then
        echo "Cannot use -standalone with -jsynth"
        exit
    fi
    
    if [ $POLY = "POLY2" ] && [ $JUCE_POLY = "1" ]; then
        echo "Cannot use -effect with -jsynth"
        exit
    fi
    
    #look for polyphonic "nvoices" metadata in the DSP file
    
    grep "declare nvoices" $FILES && IS_SYNTH="1" 2>/dev/null
    
    #-------------------------------------------------------------------
    # compile the *.dsp files
    #-------------------------------------------------------------------
    
    for p in $FILES; do
    
        CUR=$(pwd)
        f=$(basename "$p")
    	SRCDIR=$(dirname "$p")
    
        # creates the dir 
        dspName="${f%.dsp}"
        SUB_TYPE=$(shasum $p)
        SUB_TYPE=${SUB_TYPE:0:4}
        rm -rf "$SRCDIR/$dspName"
     
        if [ $STANDALONE = "0" ]; then
            cp -r $FAUSTARCH/juce/plugin "$SRCDIR/$dspName/"
            # setting plugin name to match the dsp
            sed -e "s/SUB_TYPE/$SUB_TYPE/g" "$SRCDIR/$dspName/$JUCER" >> "$SRCDIR/$dspName/$dspName-temp.jucer"
        else
            cp -r $FAUSTARCH/juce/standalone "$SRCDIR/$dspName/"
            # setting project name to match the dsp
            sed -e "s/ProjectTitle/$dspName/g" "$SRCDIR/$dspName/$JUCER" >> "$SRCDIR/$dspName/$dspName-temp.jucer"
        fi
    
        # setting the preprocessing definitions
        sed -e "s/PreProcDef/$DEF/g" "$SRCDIR/$dspName/$dspName-temp.jucer" >> "$SRCDIR/$dspName/$dspName-temp0.jucer"
        sed -e "s/APPL_NAME/$dspName/g" "$SRCDIR/$dspName/$dspName-temp0.jucer" >> "$SRCDIR/$dspName/$dspName-temp1.jucer"
    
        # MIDI
        sed -e "s/IS_MIDI/$IS_MIDI/g" "$SRCDIR/$dspName/$dspName-temp1.jucer" >> "$SRCDIR/$dspName/$dspName-temp2.jucer"
    
        # SYNTH
        sed -e "s/IS_SYNTH/$IS_SYNTH/g" "$SRCDIR/$dspName/$dspName-temp2.jucer" >> "$SRCDIR/$dspName/$dspName-temp3.jucer"
    
        # FAUSTFLOAT
        sed -e "s/FAUST_FLOAT/$FAUSTFLOAT/g" "$SRCDIR/$dspName/$dspName-temp3.jucer" >> "$SRCDIR/$dspName/$dspName-temp4.jucer"
    
        # JUCE_MODULES_DIR
        sed -e "s/JUCE_MODULES_DIR_DEFAULT/$JUCE_MODULES_DIR/g" "$SRCDIR/$dspName/$dspName-temp4.jucer" >> "$SRCDIR/$dspName/$dspName-temp5.jucer"
    
        # possibly set NVOICES value
        if [ $NVOICES -ge 0 ]; then
            sed -e "s/NUM_VOICES/$NVOICES/g" "$SRCDIR/$dspName/$dspName-temp5.jucer" >> "$SRCDIR/$dspName/$dspName.jucer"
        else
            cp "$SRCDIR/$dspName/$dspName-temp5.jucer" "$SRCDIR/$dspName/$dspName.jucer"
        fi
    
        # standalone or plugin mode
        if [ $STANDALONE = "0" ]; then
            rm "$SRCDIR/$dspName/plugin-llvm.jucer" "$SRCDIR/$dspName/plugin.jucer"
        else
            rm "$SRCDIR/$dspName/standalone-llvm.jucer" "$SRCDIR/$dspName/standalone.jucer"
        fi
      
        rm "$SRCDIR/$dspName/$dspName-temp.jucer"  "$SRCDIR/$dspName/$dspName-temp0.jucer" 
        rm "$SRCDIR/$dspName/$dspName-temp1.jucer" "$SRCDIR/$dspName/$dspName-temp2.jucer"
        rm "$SRCDIR/$dspName/$dspName-temp3.jucer" "$SRCDIR/$dspName/$dspName-temp4.jucer" 
        rm "$SRCDIR/$dspName/$dspName-temp5.jucer"
    
        # standalone of plugin mode
        if [ $STANDALONE = "0" ]; then
            if [ $IS_LLVM = "1" ]; then
                faust -inj $FAUSTINC/faust/dsp/llvm-dsp-adapter.h -uim -i -a $FAUSTARCH/juce/juce-plugin.cpp $OPTIONS "$SRCDIR/$f" -o "$SRCDIR/$dspName/FaustPluginProcessor.cpp" || exit
                dynamic-faust $OPTIONS "$SRCDIR/$f" -o "$SRCDIR/$dspName/dynamic.o"
            else
                faust -uim -i -a $FAUSTARCH/juce/juce-plugin.cpp $OPTIONS "$SRCDIR/$f" -o "$SRCDIR/$dspName/FaustPluginProcessor.cpp" || exit
            fi
        else
            if [ $IS_LLVM = "1" ]; then
                faust -inj $FAUSTINC/faust/dsp/llvm-dsp-adapter.h -i -a $FAUSTARCH/juce/juce-standalone.cpp $OPTIONS "$SRCDIR/$f" -o "$SRCDIR/$dspName/FaustAudioApplication.cpp" || exit
                dynamic-faust $OPTIONS "$SRCDIR/$f" -o "$SRCDIR/$dspName/dynamic.o"
            else
                faust -i -a $FAUSTARCH/juce/juce-standalone.cpp $OPTIONS "$SRCDIR/$f" -o "$SRCDIR/$dspName/FaustAudioApplication.cpp" || exit
            fi
        fi
    
        if [ $POLY = "POLY2" ]; then
            if [ $EFFECT = "auto" ]; then
                cat > $SRCDIR/$dspName/effect.dsp << EndOfCode
                adapt(1,1) = _;
                adapt(2,2) = _,_;
                adapt(1,2) = _ <: _,_;
                adapt(2,1) = _,_ :> _;
                adaptor(F,G) = adapt(outputs(F),inputs(G));
                process = adaptor(library("$SRCDIR/$f").process, library("$SRCDIR/$f").effect) : library("$SRCDIR/$f").effect;
    EndOfCode
                faust -i -cn effect -a minimal-effect.cpp "$SRCDIR/$dspName/effect.dsp" -o "$SRCDIR/$dspName/effect.h" || exit
                rm "$SRCDIR/$dspName/effect.dsp"
            else
                faust -i -cn effect -a minimal-effect.cpp "$SRCDIR/$EFFECT" -o "$SRCDIR/$dspName/effect.h" || exit
            fi
        fi
    
    done
    
    


  • @hisefilo Just to understand, what is the benefit of using Faust?
    For what I see, everything can be made directly with scriptnode/snex right? 🤔
    Easier than snex maybe?



  • @ustk Faust is less buggy 😛

    But I've stopped playing with Faust for now because I'd rather use the new (less buggy?) version of SNEX when it's ready.



  • @ustk it's being developed for years and a huge library is there (Many of them developed by JOS)
    i.e. you may have an analog modelled filter in just a few lines of code

    https://faust.grame.fr/tools/playground/index.html



  • @d-healey I haven't realised yet what SNEX will allow me to do. C++ is kinda obscure zone for me.

    Faust does the compile thing to turn this one line thingy into a physically modelled trumpet on C++

    process = pm.trumpet <: _,_; 
    

Log in to reply
 

8
Online

647
Users

2.6k
Topics

21.4k
Posts