HISE Logo Forum
    • Categories
    • Register
    • Login

    How does a custom c++ interface with one of its display buffers?

    Scheduled Pinned Locked Moved C++ Development
    13 Posts 4 Posters 768 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.
    • OrvillainO
      Orvillain @griffinboy
      last edited by

      @griffinboy said in How does a custom c++ interface with one of its display buffers?:

      @Orvillain

      Yeah it's possible. Take a look at the source for external data

      ExternalData.txt

      favicon

      Google Docs (drive.google.com)

      You can write to display buffers. But we warned, these are ring buffers and so personally I've had issues with it wrapping and having the wrong size, stuff like that. Christoph can probably set you straight. But the methods for writing into the data slot are in this header I think!

      Groovy, cheers!! I can't really suss it out.

      I don't need the buffer to update with the processFrame method or anything like that. I just want to effectively set its size once, and blat a bunch of data into it. I tried accessing the ring buffer using 'rb' like this:

      if (auto lock = DataReadLock(this))
      {
      	if (rb != nullptr)
      	{
      		rb->write(env.data(), static_cast<int>(env.size()));
      	}
      }
      
      But it doesn't like the data I'm trying to feed it.
      

      Musician - Instrument Designer - Sonic Architect - Creative Product Owner
      Crafting sound at every level. From strings to signal paths, samples to systems.

      OrvillainO 1 Reply Last reply Reply Quote 0
      • OrvillainO
        Orvillain @Orvillain
        last edited by

        I resorted to ChatGPT. This compiles:

        		// === Write to display buffer ===
        		if (auto* ringBuffer = dynamic_cast<SimpleRingBuffer*>(this->externalData.obj))
        		{
        			DataWriteLock lock(this); // Required to write safely
        
        			auto& targetBuffer = ringBuffer->getWriteBuffer();
        			const int numSamples = static_cast<int>(env.size());
        
        			// Resize to match env
        			targetBuffer.setSize(1, numSamples, false, false, true);
        
        			for (int i = 0; i < numSamples; ++i)
        				targetBuffer.setSample(0, i, env[i]);
        
        		}
        

        But I don't get any data displaying in the display buffer; whether I look at embedded or the 0th external slot.

        Musician - Instrument Designer - Sonic Architect - Creative Product Owner
        Crafting sound at every level. From strings to signal paths, samples to systems.

        OrvillainO HISEnbergH 2 Replies Last reply Reply Quote 0
        • OrvillainO
          Orvillain @Orvillain
          last edited by

          I ended up piping data through a global cable and plotting it to a scriptPanel - it is pretty good actually!!!

          But I would like to know how to utilise the display buffers directly on the node.

          Musician - Instrument Designer - Sonic Architect - Creative Product Owner
          Crafting sound at every level. From strings to signal paths, samples to systems.

          1 Reply Last reply Reply Quote 0
          • HISEnbergH
            HISEnberg @Orvillain
            last edited by

            @Orvillain Thanks for sharing this! Did you ever solve the initial problem of using the display buffer however? It looks like it should be relatively straightforward but I'm not able to work through the source code and find an immediate solution.

            OrvillainO 1 Reply Last reply Reply Quote 0
            • OrvillainO
              Orvillain @HISEnberg
              last edited by

              @HISEnberg I'm afraid not! I gave up after I figured out how to pipe what I needed through a global cable, and then HISEscript takes over and plots it to a script panel. So I never really went back.

              Musician - Instrument Designer - Sonic Architect - Creative Product Owner
              Crafting sound at every level. From strings to signal paths, samples to systems.

              HISEnbergH 1 Reply Last reply Reply Quote 0
              • HISEnbergH
                HISEnberg @Orvillain
                last edited by

                @Orvillain Okay no worries. I am in the same position for now (displaying compressor reduction) and managed to get the data out to a global cable so I can just draw that to a panel. I would like to know the proper way to do this for future purposes however (audio waveforms, etc.) Will post here if I figure it out!

                griffinboyG 1 Reply Last reply Reply Quote 0
                • griffinboyG
                  griffinboy @HISEnberg
                  last edited by

                  @HISEnberg

                  I think christoph would know for sure.
                  I did manage to do it but it had inconsistent quirks that seemed to do with the display buffer length not matching up with what I was sending it (Holding on to a longer history)

                  So I'm hesitant to share an incomplete solution. I'm not sure how resizing it works

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

                    That's how the peak node does it:

                    Link Preview Image
                    HISE/hi_dsp_library/dsp_nodes/CoreNodes.h at a20873ea7a8c00b251ddae7df8d6e3007f8c5f71 · christophhart/HISE

                    The open source framework for sample based instruments - HISE/hi_dsp_library/dsp_nodes/CoreNodes.h at a20873ea7a8c00b251ddae7df8d6e3007f8c5f71 · christophhart/HISE

                    favicon

                    GitHub (github.com)

                    OrvillainO 1 Reply Last reply Reply Quote 1
                    • OrvillainO
                      Orvillain @Christoph Hart
                      last edited by Orvillain

                      @Christoph-Hart

                      Just idly reading through the code. It looks relatively simple??

                      Inherit like this:

                      template <bool Unscaled>
                      class peak_base : public data::display_buffer_base<true>
                      

                      Then call this whenever you want the buffer to update:

                      updateBuffer(datagoeshere, data.getNumSamples());
                      

                      Looking at the code for display_buffer_base, there are these two functions:

                      void updateBuffer(double v, int numSamples)
                      void updateBuffer(const float* values, int numSamples)
                      

                      Looks like the first one is for per-sample updating, and the second one is for inserting an array of samples??

                      Musician - Instrument Designer - Sonic Architect - Creative Product Owner
                      Crafting sound at every level. From strings to signal paths, samples to systems.

                      OrvillainO 1 Reply Last reply Reply Quote 1
                      • OrvillainO
                        Orvillain @Orvillain
                        last edited by

                        // ==================================| Third Party Node Template |==================================
                        
                        #pragma once
                        #include <JuceHeader.h>
                        
                        namespace project
                        {
                        using namespace juce;
                        using namespace hise;
                        using namespace scriptnode;
                        
                        // ==========================| The node class with all required callbacks |==========================
                        
                        template <int NV> struct LoadAudio: public data::base, public data::display_buffer_base<true>
                        {
                        	// Metadata Definitions ------------------------------------------------------------------------
                        	
                        	SNEX_NODE(LoadAudio);
                        	
                        	struct MetadataClass
                        	{
                        		SN_NODE_ID("LoadAudio");
                        	};
                        	
                        	// set to true if you want this node to have a modulation dragger
                        	static constexpr bool isModNode() { return false; };
                        	static constexpr bool isPolyphonic() { return NV > 1; };
                        	// set to true if your node produces a tail
                        	static constexpr bool hasTail() { return false; };
                        	// set to true if your doesn't generate sound from silence and can be suspended when the input signal is silent
                        	static constexpr bool isSuspendedOnSilence() { return false; };
                        	// Undefine this method if you want a dynamic channel count
                        	static constexpr int getFixChannelAmount() { return 2; };
                        	
                        	// Define the amount and types of external data slots you want to use
                        	static constexpr int NumTables = 0;
                        	static constexpr int NumSliderPacks = 0;
                        	static constexpr int NumAudioFiles = 1;
                        	static constexpr int NumFilters = 0;
                        	static constexpr int NumDisplayBuffers = 1;
                        
                        	AudioBuffer<float> originalBuffer;
                        	int originalSampleRate;
                        	int originalNumSamples;
                        	
                        	// Scriptnode Callbacks ------------------------------------------------------------------------
                        	
                        	void prepare(PrepareSpecs specs)
                        	{
                        		
                        	}
                        	
                        	void reset()
                        	{
                        		
                        	}
                        	
                        	void handleHiseEvent(HiseEvent& e)
                        	{
                        		
                        	}
                        	
                        	template <typename T> void process(T& data)
                        	{
                        		
                        		static constexpr int NumChannels = getFixChannelAmount();
                        		// Cast the dynamic channel data to a fixed channel amount
                        		auto& fixData = data.template as<ProcessData<NumChannels>>();
                        		
                        		// Create a FrameProcessor object
                        		auto fd = fixData.toFrameData();
                        		
                        		while(fd.next())
                        		{
                        			// Forward to frame processing
                        			processFrame(fd.toSpan());
                        		}
                        		
                        	}
                        	
                        	template <typename T> void processFrame(T& data)
                        	{
                        		
                        	}
                        	
                        	int handleModulation(double& value)
                        	{
                        		
                        		return 0;
                        		
                        	}
                        	
                        	void setExternalData(const ExternalData& data, int index)
                        	{
                        		if (data.isNotEmpty())
                        		{
                        
                        			originalBuffer = data.toAudioSampleBuffer();
                        			originalSampleRate = data.sampleRate;
                        			originalNumSamples = originalBuffer.getNumSamples();
                        			
                        			const float* ch0 = originalBuffer.getReadPointer(0);
                        			updateBuffer(ch0, originalNumSamples);
                        
                        
                        		}
                        	}
                        	// Parameter Functions -------------------------------------------------------------------------
                        	
                        	template <int P> void setParameter(double v)
                        	{
                        		if (P == 0)
                        		{
                        			// This will be executed for MyParameter (see below)
                        			jassertfalse;
                        		}
                        		
                        	}
                        	
                        	void createParameters(ParameterDataList& data)
                        	{
                        		{
                        			// Create a parameter like this
                        			parameter::data p("MyParameter", { 0.0, 1.0 });
                        			// The template parameter (<0>) will be forwarded to setParameter<P>()
                        			registerCallback<0>(p);
                        			p.setDefaultValue(0.5);
                        			data.add(std::move(p));
                        		}
                        	}
                        };
                        }
                        
                        
                        
                        

                        Well I tried this... no luck so far.

                        Musician - Instrument Designer - Sonic Architect - Creative Product Owner
                        Crafting sound at every level. From strings to signal paths, samples to systems.

                        OrvillainO 1 Reply Last reply Reply Quote 0
                        • OrvillainO
                          Orvillain @Orvillain
                          last edited by

                          @Orvillain
                          3d59f862-0879-4207-8770-9f6729fd3a8b-image.png

                          huh... well I got SOMETHING in there...

                          This is just chatGPT chod, but it has given me the above. The issue is... it doesn't update immediately, I have to switch the selected display buffer in the node using the external icon button.

                          // ==================================| Third Party Node Template |==================================
                          
                          #pragma once
                          #include <JuceHeader.h>
                          
                          namespace project
                          {
                          using namespace juce;
                          using namespace hise;
                          using namespace scriptnode;
                          
                          // ==========================| The node class with all required callbacks |==========================
                          
                          template <int NV> struct LoadAudio: public data::base, public data::display_buffer_base<true>
                          {
                          	// Metadata Definitions ------------------------------------------------------------------------
                          	
                          	SNEX_NODE(LoadAudio);
                          	
                          	struct MetadataClass
                          	{
                          		SN_NODE_ID("LoadAudio");
                          	};
                          	
                          	// set to true if you want this node to have a modulation dragger
                          	static constexpr bool isModNode() { return false; };
                          	static constexpr bool isPolyphonic() { return NV > 1; };
                          	// set to true if your node produces a tail
                          	static constexpr bool hasTail() { return false; };
                          	// set to true if your doesn't generate sound from silence and can be suspended when the input signal is silent
                          	static constexpr bool isSuspendedOnSilence() { return false; };
                          	// Undefine this method if you want a dynamic channel count
                          	static constexpr int getFixChannelAmount() { return 2; };
                          	
                          	// Define the amount and types of external data slots you want to use
                          	static constexpr int NumTables = 0;
                          	static constexpr int NumSliderPacks = 0;
                          	static constexpr int NumAudioFiles = 1;
                          	static constexpr int NumFilters = 0;
                          	static constexpr int NumDisplayBuffers = 1;
                          
                          	AudioBuffer<float> originalBuffer;
                          	int originalSampleRate;
                          	int originalNumSamples;
                          	bool bufferNeedsUpdate = false;
                          
                          	// Scriptnode Callbacks ------------------------------------------------------------------------
                          	
                          	void prepare(PrepareSpecs specs) override
                          	{
                          		display_buffer_base<true>::prepare(specs);
                          
                          		if (rb != nullptr)
                          		{
                          			rb->setActive(true);
                          
                          			if (bufferNeedsUpdate && originalBuffer.getNumChannels() > 0)
                          			{
                          				updateBuffer(originalBuffer.getReadPointer(0), originalNumSamples);
                          				bufferNeedsUpdate = false;
                          			}
                          		}
                          	}
                          
                          	
                          	void reset()
                          	{
                          		
                          	}
                          	
                          	void handleHiseEvent(HiseEvent& e)
                          	{
                          		
                          	}
                          	
                          	template <typename T> void process(T& data)
                          	{
                          		
                          		static constexpr int NumChannels = getFixChannelAmount();
                          		// Cast the dynamic channel data to a fixed channel amount
                          		auto& fixData = data.template as<ProcessData<NumChannels>>();
                          		
                          		// Create a FrameProcessor object
                          		auto fd = fixData.toFrameData();
                          		
                          		while(fd.next())
                          		{
                          			// Forward to frame processing
                          			processFrame(fd.toSpan());
                          		}
                          		
                          	}
                          	
                          	template <typename T> void processFrame(T& data)
                          	{
                          		
                          	}
                          	
                          	int handleModulation(double& value)
                          	{
                          		
                          		return 0;
                          		
                          	}
                          	
                          	void setExternalData(const ExternalData& data, int index) override
                          	{
                          		display_buffer_base<true>::setExternalData(data, index);
                          
                          		if (data.isNotEmpty())
                          		{
                          			originalBuffer = data.toAudioSampleBuffer();
                          			originalSampleRate = data.sampleRate;
                          			originalNumSamples = originalBuffer.getNumSamples();
                          			bufferNeedsUpdate = true; // ✅ delay actual update
                          		}
                          	}
                          
                          	// Parameter Functions -------------------------------------------------------------------------
                          	
                          	template <int P> void setParameter(double v)
                          	{
                          		if (P == 0)
                          		{
                          			// This will be executed for MyParameter (see below)
                          			jassertfalse;
                          		}
                          		
                          	}
                          	
                          	void createParameters(ParameterDataList& data)
                          	{
                          		{
                          			// Create a parameter like this
                          			parameter::data p("MyParameter", { 0.0, 1.0 });
                          			// The template parameter (<0>) will be forwarded to setParameter<P>()
                          			registerCallback<0>(p);
                          			p.setDefaultValue(0.5);
                          			data.add(std::move(p));
                          		}
                          	}
                          };
                          }
                          
                          
                          
                          

                          Musician - Instrument Designer - Sonic Architect - Creative Product Owner
                          Crafting sound at every level. From strings to signal paths, samples to systems.

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

                          15

                          Online

                          1.8k

                          Users

                          12.1k

                          Topics

                          105.7k

                          Posts