A Collection of Useful C++ Classes for Signal Processing

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Pfft,

There's this one fork in Github: https://github.com/gerasim13/DSPFilters/
At least someone has improved it.

It would be worth fixing the library, because it's good one.

I'm willing to fix the part that's complicating my own program, because I have to get it to work, but have to brush up some C++ knowledge.

Post

Fluky wrote:Pfft,

There's this one fork in Github: https://github.com/gerasim13/DSPFilters/
At least someone has improved it.
Hmm, interesting, I haven't seen this fork before. I guess it's worth trying to see how this works.

Post

AUTO-ADMIN: Non-MP3, WAV, OGG, SoundCloud, YouTube, Vimeo, Twitter and Facebook links in this post have been protected automatically. Once the member reaches 5 posts the links will function as normal.
Hi everyone,

I am trying to implement the Vinnie Falcao's DSP Filters into a robotic platform.
I need real time processing, so I filter data by chunk-by-chunk.

When I use a 1st order Butterworth filter, the last value of the input chunk is not correctly filtered. (while filtering with higher orders I didn't have this error).

Here is a simple code to reproduce the same error :

Code: Select all (#)

	std::vector<double> chunk(10,50);      // an example of input chunk
	
	double* audioData[1];	
	audioData[0] = chunk.data();               // my double* is now considered as constant

    Dsp::FilterDesign
      <Dsp::Butterworth::Design::LowPass <1>, 1 > f;    // 1st order, 1 channel, lowpass
 
      
    Dsp::Params params;
    params[0] = 44100; 	// sample rate
    params[1] = 1; 		// order
    params[2] = 1000; 	// cutoff frequency

    f.setParams (params);

    f.process (chunk.size(), audioData);

The result is : 3.33029 9.54723 14.936 19.6069 23.6556 27.165 30.2069 32.8436 35.129 33.7797
It should be : 3.33029 9.54723 14.936 19.6069 23.6556 27.165 30.2069 32.8436 35.129 37.11

As a result, the next chunk is not filtered correctly neither.

Does anybody have already faced with this problem, or am I missing something ?

Thank you in advance for your answers.

Antonyo Musabini

Post

Fluky wrote:It would be worth fixing the library, because it's good one.
The thing is that you inherit the original design if you want to keep compatibility. And I'd rather develop my own toolkit (with its own advantages and also drawbacks, of course, everything is a matter of compromise) than trying to understand someone else's. Which is what I did.

Post

Miles1981 wrote:
Fluky wrote:It would be worth fixing the library, because it's good one.
The thing is that you inherit the original design if you want to keep compatibility. And I'd rather develop my own toolkit (with its own advantages and also drawbacks, of course, everything is a matter of compromise) than trying to understand someone else's. Which is what I did.
So you're working on a library that might replace this one in the future?
And you claim that your architecture is better?

Does it have parameter smoothing already similar to DSPFilters?

I could probably implement the BandShelf faster by writing my own SmoothFilterDesign class, but since the DSPFilters has so much in it already, it would be a shame to leave it that way. Unless its designs can be easily transformed to another attempt at a similar library.

Post

No, I don't have parameter smoothing (I guess you are refering to the class you were trying to use?), but it is straightforward to implement something like that in Audio Toolkit. You just need to implement a crossover as the summation filter. I may implement it if there is a need for it.
And yes, I think my library can easily replace DSPFilter. But the design is completely different.

Post

Well. Since I'm in need of a parameter smoothing Butterworth band shelf, then can you implement those in AudioTK?

Post

I'll have to check... If I understand what you want, it's that when a parameter changes in the filter, the effects are smoothed over some samples? Could be achieved with the TimeVarying IIR (it precomputes a set of coeffs if the frequency changes), but it's not straightforward (so that the whole sets of parameters are not recomputed each time and so that the state is not reseted after each change).

Post

Miles1981 wrote:I'll have to check... If I understand what you want, it's that when a parameter changes in the filter, the effects are smoothed over some samples?
Yes. And the implementation of Dsp:SmoothFilterDesign is a good one, there you can set the amount of samples that you want it to transition over. I don't know how it works though.

Post

Well, it's quite crude and slow, but it works: https://github.com/gerasim13/DSPFilters ... edFilter.h

My design doesn't allow me to do exactly the same, but ideally it can be done if you want to recompute all coefficients each time (which is quite time consuming, as there is at least one tan() function inside!). But I need to think carefully about how to do this efficiently without having to rewrite all the coefficients computations.

Post

antonyomusabini wrote: When I use a 1st order Butterworth filter, the last value of the input chunk is not correctly filtered. (while filtering with higher orders I didn't have this error).
I don't use the filters for real time processing, so I can't speak from experience. However, I just rebuilt and ran DSPFiltersDemo. I set it to the 440 Hz sine wave and lower cutoffs. If there was an error there would be noticeable pops in the output but it played smoothly.

What compiler are you using and platform are you building for?

Post

Fluky wrote:Yes. And the implementation of Dsp:SmoothFilterDesign is a good one, there you can set the amount of samples that you want it to transition over. I don't know how it works though.
Since DSPFiltersDemo compiles and works, I think that would be a good start. From what I can determine looking at MainPanel.cpp, it appears that the "Smooth" filters want 2 filters, the current filter and the filter you are transitioning to and the number of samples to transition over. It creates a new filter and copies params[] over intelligently in the setup.

Post

The SmoothFilter only wants one filter, not two. It can work for this library this way because all the coefficients are set via a vector and not by name. Doing so in ATK would be overkill...
So there are two solutions for you at the moment if you want to switch to ATK:
- use the TimeVaryingIIRFilters and feed in a new vector with the frequency you want to filter. Unfortunatelt, there are not as many filter designs for this one as there are for the usual IIRFilter. The good side is that it is in direct form 2
- use the IIRFilter and make the transition yourself, at the level of the pipeline. It's not reusable and IIRFilter is not DirectForm2 (but it is easy to implement a new one that is and then reuse the coefficient computation classes).

Option 2 would close to this (just use a InPointer and OutPointer as in the ATK plugins):

Code: Select all

ATK::SinusGeneratorFilter<double> generator;
  generator.set_output_sampling_rate(1024*64);
  generator.set_amplitude(1);
  generator.set_frequency(100);
  
  ATK::IIRFilter<ATK::BesselLowPassCoefficients<double> > filter;
  filter.set_input_sampling_rate(1024*64);
  filter.set_output_sampling_rate(1024*64);
  filter.set_cut_frequency(100);
  filter.set_order(3);


void process(int64_t size)
{
// let's say that I want to change the cut_frequency to new_cut_frequency
// For this, I set this variable as well as old_cut_frequency (former cut_frequency) and remaining_transition_samples to 1024
// I'll process new samples by chunks of 32 elements
// I should do something more clever to also track how many of the 32 samples were processed last time

int processing_samples = std::min (remaining_transition_samples, size);

if (processing_samples > 0)
{
  double taper = (new_cut_frequency - old_cut_frequency) * 32. / remaining_transition_samples;
  for(int i = 0; i < processing_samples; i += 32)
  {
    filter->set_cut_frequency(i/32 * taper + old_cut_frequency)
    filter->process(std::min(32, processing_samples - i));
  }
  remaining_transition_samples -= processing_samples;
  old_cut_frequency = filter->get_cut_frequency();
}
if(remaining_transition_samples = 0)
{
  remaining_transition_samples = -1;
  filter->set_cut_frequency(new_cut_frequency);
}
if(processing_samples < size)
{
  filter->process(size - processing_samples);
}

}
  
Haven't tested it, but something like that would do the trick.

Post

Miles1981 wrote:The SmoothFilter only wants one filter, not two. It can work for this library this way because all the coefficients are set via a vector and not by name. Doing so in ATK would be overkill...
So there are two solutions for you at the moment if you want to switch to ATK:
- use the TimeVaryingIIRFilters and feed in a new vector with the frequency you want to filter. Unfortunatelt, there are not as many filter designs for this one as there are for the usual IIRFilter. The good side is that it is in direct form 2
- use the IIRFilter and make the transition yourself, at the level of the pipeline. It's not reusable and IIRFilter is not DirectForm2 (but it is easy to implement a new one that is and then reuse the coefficient computation classes).

Option 2 would close to this (just use a InPointer and OutPointer as in the ATK plugins):

Code: Select all

ATK::SinusGeneratorFilter<double> generator;
  generator.set_output_sampling_rate(1024*64);
  generator.set_amplitude(1);
  generator.set_frequency(100);
  
  ATK::IIRFilter<ATK::BesselLowPassCoefficients<double> > filter;
  filter.set_input_sampling_rate(1024*64);
  filter.set_output_sampling_rate(1024*64);
  filter.set_cut_frequency(100);
  filter.set_order(3);


void process(int64_t size)
{
// let's say that I want to change the cut_frequency to new_cut_frequency
// For this, I set this variable as well as old_cut_frequency (former cut_frequency) and remaining_transition_samples to 1024
// I'll process new samples by chunks of 32 elements
// I should do something more clever to also track how many of the 32 samples were processed last time

int processing_samples = std::min (remaining_transition_samples, size);

if (processing_samples > 0)
{
  double taper = (new_cut_frequency - old_cut_frequency) * 32. / remaining_transition_samples;
  for(int i = 0; i < processing_samples; i += 32)
  {
    filter->set_cut_frequency(i/32 * taper + old_cut_frequency)
    filter->process(std::min(32, processing_samples - i));
  }
  remaining_transition_samples -= processing_samples;
  old_cut_frequency = filter->get_cut_frequency();
}
if(remaining_transition_samples = 0)
{
  remaining_transition_samples = -1;
  filter->set_cut_frequency(new_cut_frequency);
}
if(processing_samples < size)
{
  filter->process(size - processing_samples);
}

}
  
Haven't tested it, but something like that would do the trick.
I think I might switch to ATK, but can you get the Butterworth BandShelf there as well?

Post

I didn't try the bandshelf :/ I had the equations for the usual low pass, high pass, band pass and band stop. I'll have to check for the band shelf (more or less just add a fraction of the input!).
As I've said, the current IIRFilter is not Direct Form 2, so there is also some work that I can there. Let me check tonight (I first have to fix my side chain compressor uploads...) if I can make something.

Post Reply

Return to “DSP and Plugin Development”