HISE Logo Forum
    • Categories
    • Register
    • Login

    SNEX weirdness or me ?

    Scheduled Pinned Locked Moved General Questions
    22 Posts 2 Posters 755 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.
    • ustkU
      ustk
      last edited by ustk

      I'm on it since yesterday. I can't seem to get the variables to work in the processFilter member past the very first one. Check the DataTable capture, alpha is well set, sigma1 updates, but not S_alpha and the following y, z1 despite the trivial equations in the function... ?

      Screenshot 2024-10-09 at 15.24.57.png

      class MyFilter
      {
      public:
      	double processFilter(double s)
      	{
      		sigma1 = s + z1;
      		s_alpha = sigma1 * alpha;
      		y = s_alpha + z1;
      		
      		z1 = s_alpha + y;
      		
      		return y;
      	}
      	
      private:
      	double sigma1 = 0.0;
      	double s_alpha = 0.0;
      	double y = 0.0;
      	double z1 = 0.0;
      };
      
      
      span<MyFilter, NUM_CHANNELS> myFilter;
      
      using IndexType = index::clamped<NUM_CHANNELS, false>;
      IndexType idx;
      
      
      // Process
      template <int C> void processFrame(span<float, C>& data)
      {
      	const int nbChannels = Math.min(C, NUM_CHANNELS);
      	
      	for (int i = 0; i<nbChannels; i++)
      	{
      		idx = i;
      		data[idx] = (float)myFilter[idx].processFilter((double)data[idx]);
      	}
      }
      

      I extracted the variable declarations from the member function to be private, so they appear in the data table. But the behaviour is the same anyway.

      Sometimes I can get the first channel working, but not the second, not knowing why and unable to produce the weirdness reliably

      Can't help pressing F5 in the forum...

      ustkU 1 Reply Last reply Reply Quote 0
      • ustkU
        ustk @ustk
        last edited by ustk

        Also sometimes when making a few changes and going back before the changes, what worked at that time doesn't work anymore... It's like a few things are staying in memory maybe, anyway it is really strange and not reliable.

        With the console not being very helpful, the lack of auto-complete, SNEX definitely needs some interventions
        I wonder if I shouldn't go with C++ node instead, in the end for simple algorithms it's not that different in terms of difficulty. But the realtime compilation of SNEX is sexy enough to make me stay home...

        Can't help pressing F5 in the forum...

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

          @ustk Where is alpha defined? It's not visible to the inner class and I get the correct compile error "alpha can't be resolved" if I paste in your code into a SNEX node.

          ustkU 1 Reply Last reply Reply Quote 0
          • ustkU
            ustk @Christoph Hart
            last edited by ustk

            @Christoph-Hart Oh yes it was defined below, along with other things...

            Anyway I changed this code quite a bit, and at the same time I am training myself to use the sdouble type. I am still struggling but I can get the class to work it seems, so I'll come back to report

            Can't help pressing F5 in the forum...

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

              @ustk you only have to use sdouble for values that come from the outside and need to be smoothed, for internal filter coefficients or state variables that hold the last sample it's counterproductive.

              ustkU 1 Reply Last reply Reply Quote 1
              • ustkU
                ustk @Christoph Hart
                last edited by ustk

                @Christoph-Hart Ok so here's the problem I have
                As you can see, in the second span element, sigma updates, but not the other variables

                I know how to run this as separate channels within the block processing just by using a span of last value, but I wanted to implement a filter as class

                I am doing it already successfully for another DSP, but here I can't get it to work
                What is the silly thing I am doing right?

                Also:

                • Would it be possible to get the static const to show up in the table?
                • How come the non static variables are initialised to 0. if they are not set a second time in prepare ? (I'm referring to fs, T, twoOverT...)

                filterSpan.gif

                template <int NV> struct VA_LPF_1st_v2
                {
                	SNEX_NODE(VA_LPF_1st_v2);
                	
                	enum Parameters
                	{
                		Freq = 0
                	};
                	
                	
                	class MyFilter
                	{
                	public:
                	
                		double processFilter(double s)
                		{
                			sigma1 = s - z1;
                			s_alpha = sigma1 * a0;
                			y = s_alpha + z1;
                			
                			z1 = s_alpha + y;
                			
                			return y;
                		}
                	
                	private:
                	
                		double sigma1 = 0.0;
                		double s_alpha = 0.0;
                		double y = 0.0;
                		double z1 = 0.0;
                	};
                	
                	
                	
                	//! ================================================== Prepare & Reset ================================================
                	
                	void updateCoefficients()
                	{
                		if(fs > 0.0)
                		{
                			const double wa = twoOverT * Math.tan((TWO_PI * cutoff * T) / 2.0);
                			
                			a0 = (wa * T) / 2.0;
                		}
                	}
                	
                	
                	void prepare(PrepareSpecs ps)
                	{
                		if(fs != ps.sampleRate)
                		{
                			fs = ps.sampleRate;
                			T = 1.0 / fs;
                			twoOverT = 2.0 * fs;
                			
                			a0 = 0.0;
                			
                			updateCoefficients();
                		}
                	}
                	
                	
                	void reset()
                	{
                		
                	}
                	
                	//! ============================================== Process the signal here ==============================================
                	// Process the signal as block here
                	template <typename ProcessDataType> void process(ProcessDataType& data)
                	{
                		const int numChan = Math.min(data.getNumChannels(), MAX_NUM_CHANNELS);
                		
                		for(int i = 0; i < numChan; i++)
                		{
                			idx = i;
                			
                			for(auto& s: data[i])
                			{
                				s = (float)vaLpfFirst[idx].processFilter((double)s);
                			}
                		}
                	}
                	
                	
                	
                	// Process the signal as frame here
                	template <int C> void processFrame(span<float, C>& data) { }
                		
                		
                	
                	//! ===================================================== Parameters ===================================================
                	// Set the parameters here
                	template <int P> void setParameter(double v)
                	{
                		if (P == Parameters::Freq)
                		{
                			cutoff = Math.range(v, 20.0, 20000.0);
                			updateCoefficients();	
                		}
                	}
                	
                	
                	
                	void handleHiseEvent(HiseEvent& e) { }
                	void setExternalData(const ExternalData& d, int index) { }
                	
                	
                	
                	
                private:
                
                	static const int MAX_NUM_CHANNELS = 2;
                	static const double TWO_PI = 2.0 * Math.PI;
                	
                	span<MyFilter, MAX_NUM_CHANNELS> vaLpfFirst;
                	
                	using IndexType = index::clamped<MAX_NUM_CHANNELS, false>;
                	IndexType idx;
                	
                	double fs = 48000.0;
                	double T = 1.0 / 48000.0;
                	double twoOverT = 2.0 * 48000.0;
                	
                	double cutoff = 1000.0;
                	double a0 = 0.0;
                };
                

                Can't help pressing F5 in the forum...

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

                  • Your TwoOverT is in fact TwoTimesT. This blows up the filter immediately :)
                  • You're still using an outer class variable in processFilter (in this case a0). The error is that SNEX compiles and doesn't complain, not that it doesn't work. I'll try to isolate this issue in the playground and make a test for it.
                  Christoph HartC ustkU 2 Replies Last reply Reply Quote 0
                  • Christoph HartC
                    Christoph Hart @Christoph Hart
                    last edited by

                    So I could dumb it down to a minimal test case which is indeed failing:

                    struct Outer
                    {
                    	struct Inner
                    	{
                    		int processInner() const
                    		{
                    			// this should not compile,
                    			// the variable resolver must detect that we're
                    			// in the inner class and not the Outer
                    			return a0;
                    		}
                    		
                    		int value = 0;
                    	};
                    	
                    	Inner data1;
                    	
                    	int a0 = 12;
                    
                    	int processOuter()
                    	{
                    		return data1.processInner();
                    	}
                    };
                    
                    Outer outer;
                    
                    int main(int input)
                    {
                    	return outer.processOuter();
                    }
                    

                    So the parser does not detect that it's an illegal operation and just creates a pointer with a 4 byte offset (as it would for the valid member access). This then works with the first instance because the memory address of data1 is the same as the Outer object itself, but it will not work with other memory addresses (this is why your filter failed at the second channel).

                    Now the fix shouldn't be too difficult but it's been quite some time that I crawled around in the internals of the SNEX compiler...

                    1 Reply Last reply Reply Quote 0
                    • ustkU
                      ustk @Christoph Hart
                      last edited by

                      @Christoph-Hart said in SNEX weirdness or me ?:

                      • Your TwoOverT is in fact TwoTimesT. This blows up the filter immediately :)

                      Nope, T = 1/fs, TwoOverT = 2 / (1 / fs) => simplifies in 2*fs

                      • You're still using an outer class variable in processFilter (in this case a0).

                      I am trying to keep common/shared things together and separate what it needs to be. Now I could place a0 as class member but would it be better or not, this I don't know. :smiling_face_with_halo:

                      I was thinking about a shared memory issue or something related, but as for the technicality of it 😬

                      In the case of such simple DSP like filter, harmonics, etc... Would using struct over class change anything in the context of SNEX?

                      Can't help pressing F5 in the forum...

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

                        Nope, T = 1/fs, TwoOverT = 2 / (1 / fs) => simplifies in 2*fs

                        One formula (yours) is blowing the filter into NaNs immediately and one formula (mine) makes the filter work ok(ish) - it still blows up if the cutoff frequency goes near Nyquist.

                        I am trying to keep common/shared things together and separate what it needs to be.

                        Yes that's a good approach, all you need to do to make it work is to add another argument to the processFilter() function where you pass in a0 from the outer class. It's just not legal C++ to access the outer member from the inner class.

                        ustkU 1 Reply Last reply Reply Quote 1
                        • ustkU
                          ustk @Christoph Hart
                          last edited by

                          @Christoph-Hart said in SNEX weirdness or me ?:

                          One formula (yours) is blowing the filter into NaNs immediately and one formula (mine) makes the filter work ok(ish) - it still blows up if the cutoff frequency goes near Nyquist.

                          Yeah I got that blowing which is part of this algorithm (Pirkle/Zavalishin) so it is mandatory to OS at a minimum of x2, and only then I could stabilise it

                          It's just not legal C++ to access the outer member from the inner class.

                          Oh thanks! That is quite an important hint!

                          Can't help pressing F5 in the forum...

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

                            @ustk should be fixed - the compiler will scream at you now…

                            ustkU 1 Reply Last reply Reply Quote 1
                            • ustkU
                              ustk @Christoph Hart
                              last edited by

                              @Christoph-Hart So in the end I was doing something silly that didn't need a fix if I had followed the conventional approach of C++ 🤗

                              Can't help pressing F5 in the forum...

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

                                @ustk yes, but it's the job of the compiler to tell you that you're doing something stupid and the actual effect it had on your class was totally unpredictable, so it's definitely better now than before :)

                                ustkU 1 Reply Last reply Reply Quote 1
                                • ustkU
                                  ustk @Christoph Hart
                                  last edited by ustk

                                  @Christoph-Hart Hmmm... So now it complains on the index type in the process template

                                  Screenshot 2024-10-13 at 22.50.47.png

                                  private:
                                  	using IndexType = index::clamped<MAX_NUM_CHANNELS, false>;
                                  	IndexType idx;
                                  

                                  But it is impossible to stick it as member parameter or declare it as constant, am I right?
                                  So what would be the procedure then?
                                  Sorry for the C++ crash course I still need...

                                  Can't help pressing F5 in the forum...

                                  ustkU 1 Reply Last reply Reply Quote 0
                                  • ustkU
                                    ustk @ustk
                                    last edited by

                                    @Christoph-Hart both process and idx are in the same template so this shouldn't be an issue if I am not mistaken

                                    Can't help pressing F5 in the forum...

                                    ustkU 1 Reply Last reply Reply Quote 0
                                    • ustkU
                                      ustk @ustk
                                      last edited by

                                      @ustk Boing boing 🙂

                                      Can't help pressing F5 in the forum...

                                      ustkU 1 Reply Last reply Reply Quote 0
                                      • ustkU
                                        ustk @ustk
                                        last edited by ustk

                                        @Christoph-Hart Even doing this throws the error:

                                        Screenshot 2024-10-15 at 12.20.09.png

                                        It seems index is considered to be outside

                                        Can't help pressing F5 in the forum...

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

                                          @ustk ah yeah actually I caught a similar error with the index classes in my test suite when I implemented the fix but it seems that the test suite didn‘t cover index usage within a class.

                                          Back to the SNEX compiler internals I guess…

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

                                            Alright, please try again, now it should not mess with the index templates anymore.

                                            ustkU 2 Replies Last reply Reply Quote 1
                                            • First post
                                              Last post

                                            35

                                            Online

                                            1.8k

                                            Users

                                            12.1k

                                            Topics

                                            105.0k

                                            Posts