Polyphase Filters

Type : polyphase filters, used for up and down-sampling
References : C++ source code by Dave from Muon Software
Linked file : BandLimit.cpp
(this linked file is included below)
Linked file : BandLimit.h
(this linked file is included below)
Comments
from : saionara_tagushi[AT]web[DOT]de
comment : can someone give me a hint for a paper where this stuff is from?

from : ABC
comment : From there: http://www.cmsa.wmin.ac.uk/~artur/Poly.html There is also this library, implementing the same filter, but optimised for down/upsampling and ported to SSE and 3DNow!: http://ldesoras.free.fr/prod.html#src_hiir

from : bob[AT]yahoot[DOT]com
comment : Hello, I'm getting the high pass from the function by subtracting the 'oldout' variable. output=(filter_a->process(input)-oldout)*0.5; But this does not make an ideal QMF, I'm getting pass-band aliasing, so I guessing the phase is off slightly between the low and high. Is this the correct way of getting the high band? Cheers, Dave P

from : rob[DOT]belcham[AT]zen[DOT]co[DOT]uk
comment : There is an error in the 12th order, steep filter coefficients. Having checked the output against that produced by HIIR (see previous comment), i have identified the source of the error - the 4th b coefficient 0.06329609551399348, should be 0.6329609551399348.

from : 1nf0[AT]aud1osp1llag3[DOT]bot[DOT]com
comment : some questions.. is it normal for these halfband filters to cause significant gain loss? sonogram analysis shows a decrease in SNR if I have read the results correctly. if using these filters for oversampling and I do this: upsample halfband filter *process* halfband filter decimate (discard samples) then presumably the second halfband filter does the antialiasing at half the new sampling rate?

from : bla
comment : you also need to delete the pointers inside the array CAllPassFilterCascade::~CAllPassFilterCascade() { for (int i=0;i<numfilters;i++) { delete allpassfilter[i]; } delete[] allpassfilter; };

from : bobby
comment : Is the cutoff at 20kHz? What sample rate are these coefficients for? How would I calculate suitable coefficients for arbitrary sample rates?

from : sean[AT]valhalladsp[DOT]com
comment : It is worth noting that if these filters are being used for upsampling/downsampling, the "noble identity" can be used to reduce the CPU cost. The basic idea is that operations that can be expressed in the form: filter that uses z^-N for its states -> downsample by N can be rearranged to use the form downsample by N -> filter that uses z^-1 for its states The same property holds true for upsampling. See http://mue.music.miami.edu/thesis/jvandekieft/jvchapter3.htm for more details. For the above code, this would entail creating an alternative allpass process function, that uses the z^-1 for its states, and then rearranging some of the operations.


Linked files
CAllPassFilter::CAllPassFilter(const double coefficient)
{
	a=coefficient;

	x0=0.0;
	x1=0.0;
	x2=0.0;
	
	y0=0.0;
	y1=0.0;
	y2=0.0;

};


CAllPassFilter::~CAllPassFilter()
{
};


double CAllPassFilter::process(const double input)
{
	//shuffle inputs
	x2=x1;
	x1=x0;
	x0=input;

	//shuffle outputs
	y2=y1;
	y1=y0;

	//allpass filter 1
	const double output=x2+((input-y2)*a);

	y0=output;

	return output;
};


CAllPassFilterCascade::CAllPassFilterCascade(const double* coefficient, const int N)
{
	allpassfilter=new CAllPassFilter*[N];

	for (int i=0;i<N;i++)
	{
		allpassfilter[i]=new CAllPassFilter(coefficient[i]);
	}

	numfilters=N;
};


CAllPassFilterCascade::~CAllPassFilterCascade()
{
	delete[] allpassfilter;
};


double CAllPassFilterCascade::process(const double input)
{
	double output=input;
	
	int i=0;

	do
	{
		output=allpassfilter[i]->process(output);
		i++;

	} while (i<numfilters);

	return output;
};


CHalfBandFilter::CHalfBandFilter(const int order, const bool steep)
{
	if (steep==true)
	{
		if (order==12)	//rejection=104dB, transition band=0.01
		{
			double a_coefficients[6]=
			{0.036681502163648017
			,0.2746317593794541
			,0.56109896978791948
			,0.769741833862266
			,0.8922608180038789
			,0.962094548378084
			};

			double b_coefficients[6]=
			{0.13654762463195771
			,0.42313861743656667
			,0.6775400499741616
			,0.839889624849638
			,0.9315419599631839
			,0.9878163707328971
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,6);
			filter_b=new CAllPassFilterCascade(b_coefficients,6);
		}
		else if (order==10)	//rejection=86dB, transition band=0.01
		{
			double a_coefficients[5]=
			{0.051457617441190984
			,0.35978656070567017
			,0.6725475931034693
			,0.8590884928249939
			,0.9540209867860787
			};

			double b_coefficients[5]=
			{0.18621906251989334
			,0.529951372847964
			,0.7810257527489514
			,0.9141815687605308
			,0.985475023014907
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,5);
			filter_b=new CAllPassFilterCascade(b_coefficients,5);
		}
		else if (order==8)	//rejection=69dB, transition band=0.01
		{
			double a_coefficients[4]=
			{0.07711507983241622
			,0.4820706250610472
			,0.7968204713315797
			,0.9412514277740471
			};

			double b_coefficients[4]=
			{0.2659685265210946
			,0.6651041532634957
			,0.8841015085506159
			,0.9820054141886075
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,4);
			filter_b=new CAllPassFilterCascade(b_coefficients,4);
		}
		else if (order==6)	//rejection=51dB, transition band=0.01
		{
			double a_coefficients[3]=
			{0.1271414136264853
			,0.6528245886369117
			,0.9176942834328115
			};

			double b_coefficients[3]=
			{0.40056789819445626
			,0.8204163891923343
			,0.9763114515836773
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,3);
			filter_b=new CAllPassFilterCascade(b_coefficients,3);
		}
		else if (order==4)	//rejection=53dB,transition band=0.05
		{
			double a_coefficients[2]=
			{0.12073211751675449
			,0.6632020224193995
			};

			double b_coefficients[2]=
			{0.3903621872345006
			,0.890786832653497
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,2);
			filter_b=new CAllPassFilterCascade(b_coefficients,2);
		}
	
		else	//order=2, rejection=36dB, transition band=0.1
		{
			double a_coefficients=0.23647102099689224;
			double b_coefficients=0.7145421497126001;

			filter_a=new CAllPassFilterCascade(&a_coefficients,1);
			filter_b=new CAllPassFilterCascade(&b_coefficients,1);
		}
	}
	else	//softer slopes, more attenuation and less stopband ripple
	{
		if (order==12)	//rejection=150dB, transition band=0.05
		{
			double a_coefficients[6]=
			{0.01677466677723562
			,0.13902148819717805
			,0.3325011117394731
			,0.53766105314488
			,0.7214184024215805
			,0.8821858402078155
			};

			double b_coefficients[6]=
			{0.06501319274445962
			,0.23094129990840923
			,0.4364942348420355
			,0.06329609551399348
			,0.80378086794111226
			,0.9599687404800694
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,6);
			filter_b=new CAllPassFilterCascade(b_coefficients,6);
		}
		else if (order==10)	//rejection=133dB, transition band=0.05
		{
			double a_coefficients[5]=
			{0.02366831419883467
			,0.18989476227180174
			,0.43157318062118555
			,0.6632020224193995
			,0.860015542499582
			};

			double b_coefficients[5]=
			{0.09056555904993387
			,0.3078575723749043
			,0.5516782402507934
			,0.7652146863779808
			,0.95247728378667541
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,5);
			filter_b=new CAllPassFilterCascade(b_coefficients,5);
		}
		else if (order==8)	//rejection=106dB, transition band=0.05
		{
			double a_coefficients[4]=
			{0.03583278843106211
			,0.2720401433964576
			,0.5720571972357003
			,0.827124761997324
			};

			double b_coefficients[4]=
			{0.1340901419430669
			,0.4243248712718685
			,0.7062921421386394
			,0.9415030941737551
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,4);
			filter_b=new CAllPassFilterCascade(b_coefficients,4);
		}
		else if (order==6)	//rejection=80dB, transition band=0.05
		{
			double a_coefficients[3]=
			{0.06029739095712437
			,0.4125907203610563
			,0.7727156537429234
			};

			double b_coefficients[3]=
			{0.21597144456092948
			,0.6043586264658363
			,0.9238861386532906
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,3);
			filter_b=new CAllPassFilterCascade(b_coefficients,3);
		}
		else if (order==4)	//rejection=70dB,transition band=0.1
		{
			double a_coefficients[2]=
			{0.07986642623635751
			,0.5453536510711322
			};

			double b_coefficients[2]=
			{0.28382934487410993
			,0.8344118914807379
			};
	
			filter_a=new CAllPassFilterCascade(a_coefficients,2);
			filter_b=new CAllPassFilterCascade(b_coefficients,2);
		}
	
		else	//order=2, rejection=36dB, transition band=0.1
		{
			double a_coefficients=0.23647102099689224;
			double b_coefficients=0.7145421497126001;

			filter_a=new CAllPassFilterCascade(&a_coefficients,1);
			filter_b=new CAllPassFilterCascade(&b_coefficients,1);
		}
	}

	oldout=0.0;
};


CHalfBandFilter::~CHalfBandFilter()
{
	delete filter_a;
	delete filter_b;
};


double CHalfBandFilter::process(const double input)
{
	const double output=(filter_a->process(input)+oldout)*0.5;
	oldout=filter_b->process(input);

	return output;
}

class CAllPassFilter
{
public:

	CAllPassFilter(const double coefficient);
	~CAllPassFilter();
	double process(double input);

private:
	double a;

	double x0;
	double x1;
	double x2;

	double y0;
	double y1;
	double y2;
};


class CAllPassFilterCascade
{
public:
	CAllPassFilterCascade(const double* coefficients, int N);
	~CAllPassFilterCascade();

	double process(double input);

private:
	CAllPassFilter** allpassfilter;
	int numfilters;
};


class CHalfBandFilter
{
public:
	CHalfBandFilter(const int order, const bool steep);
	~CHalfBandFilter();

	double process(const double input);

private:
	CAllPassFilterCascade* filter_a;
	CAllPassFilterCascade* filter_b;
	double oldout;
};