[Scummvm-cvs-logs] SF.net SVN: scummvm: [28612] scummvm/branches/gsoc2007-mixer/sound

dogmatixman at users.sourceforge.net dogmatixman at users.sourceforge.net
Tue Aug 14 14:42:56 CEST 2007


Revision: 28612
          http://scummvm.svn.sourceforge.net/scummvm/?rev=28612&view=rev
Author:   dogmatixman
Date:     2007-08-14 05:42:56 -0700 (Tue, 14 Aug 2007)

Log Message:
-----------
Allows rational resampling factors, and chooses a "nice" filter length (that divides neatly into subfilters).  Filter generation delays need improving.

Modified Paths:
--------------
    scummvm/branches/gsoc2007-mixer/sound/filter.cpp
    scummvm/branches/gsoc2007-mixer/sound/filter.h
    scummvm/branches/gsoc2007-mixer/sound/rate.cpp

Modified: scummvm/branches/gsoc2007-mixer/sound/filter.cpp
===================================================================
--- scummvm/branches/gsoc2007-mixer/sound/filter.cpp	2007-08-14 08:49:20 UTC (rev 28611)
+++ scummvm/branches/gsoc2007-mixer/sound/filter.cpp	2007-08-14 12:42:56 UTC (rev 28612)
@@ -78,25 +78,31 @@
 
 /*
  * Estimated window length using the Kaiser design formula.
- * Assumption: window length <= 65535 taps.
  */
-inline uint16 FIRFilter::windowLength(
+inline uint32 FIRFilter::windowLength(
 					double ripple,
 					double transitionBW,
-					uint16 samplingFreq) {
-	double length = (samplingFreq * (-ripple - 7.95)) / (14.36 * transitionBW);
+					uint32 samplingFreq,
+					uint16 upFactor) {
+	double idealLength = (samplingFreq * (-ripple - 7.95)) / (14.36 * transitionBW);
 	
-	assert(length > 0);
+	assert(idealLength > 0);
 	
-	return (uint16)(length + 1);
+	/* Round length up to a multiple of the upsampling rate */
+	uint32 length = (uint32)(idealLength + 1);
+	if (length % upFactor) {
+		length += upFactor - (length % upFactor);
+	}
+	
+	return length;
 }
 
 /*
  * Derives the coefficients for a Kaiser window of the given properties, using
  * standard Kaiser design formulae.
  */
-void FIRFilter::windowDesign(double *coeffs, uint16 length, double ripple) {
-	uint16 i;
+void FIRFilter::windowDesign(double *coeffs, uint32 length, double ripple) {
+	uint32 i;
 	
 	double alpha = -ripple;
 	double beta;
@@ -110,7 +116,7 @@
 	}
 	
 	for (i = 0; i <= (length - 1) / 2; i++) {
-		double n = i - (length - 1) / 2 - ((length % 2) ? 0 : 0.5);
+		double n = (int32)(i - (length - 1) / 2) - ((length % 2) ? 0 : 0.5);
 		
 		/*
 		 * Both this window and the later filter are even symmetric, so we
@@ -137,14 +143,14 @@
  */
 void FIRFilter::LPDesign(
 		double *coeffs,
-		uint16 length,
+		uint32 length,
 		double passbandEdge,
 		double stopbandEdge,
-		uint16 samplingFreq) {
-	uint16 i;
+		uint32 samplingFreq) {
+	uint32 i;
 	
 	for (i = 0; i <= (length - 1) / 2; i++) {
-		double n = i - (length - 1) / 2 - ((length % 2) ? 0 : 0.5);
+		double n = (int32)(i - (length - 1) / 2) - ((length % 2) ? 0 : 0.5);
 		
 		/*
 		 * Use an ideal transition halfway between the passband and stopband
@@ -162,17 +168,19 @@
 /* Generates lowpass filter coefficients using the parameters provided */
 FIRFilter::FIRFilter(double passbandEdge, double stopbandEdge,
 					double dBPassbandRipple, double dBStopbandAtten,
-					uint16 samplingFreq)
+					uint32 samplingFreq, uint16 upFactor)
 		: _passbandEdge(passbandEdge), _stopbandEdge(stopbandEdge),
 		  _dBPassbandRipple(dBPassbandRipple),
 		  _dBStopbandAtten(dBStopbandAtten),
-		  _samplingFreq(samplingFreq), _ripple(0), _length(0), _coeffs(0) {
+		  _samplingFreq(samplingFreq),
+		  _upFactor(upFactor), _ripple(0), _length(0), _coeffs(0) {
 	/* Find the amount of ripple that the filter will produce */
 	_ripple = equiripple(_dBPassbandRipple, _dBStopbandAtten);
 	
 	/* Find the number of coefficients in the window */
 	_length = 
-		windowLength(_ripple, _stopbandEdge - _passbandEdge, _samplingFreq);
+		windowLength(_ripple, _stopbandEdge - _passbandEdge, _samplingFreq,
+					 _upFactor);
 	
 	/* Calculate the window coefficients */
 	_coeffs = (double *)malloc(_length * sizeof(double));

Modified: scummvm/branches/gsoc2007-mixer/sound/filter.h
===================================================================
--- scummvm/branches/gsoc2007-mixer/sound/filter.h	2007-08-14 08:49:20 UTC (rev 28611)
+++ scummvm/branches/gsoc2007-mixer/sound/filter.h	2007-08-14 12:42:56 UTC (rev 28612)
@@ -37,10 +37,11 @@
 	double _stopbandEdge;
 	double _dBPassbandRipple;
 	double _dBStopbandAtten;
-	uint16 _samplingFreq;
+	uint32 _samplingFreq;
+	uint16 _upFactor;
 	
 	double _ripple;
-	uint16 _length;
+	uint32 _length;
 	double *_coeffs;
 
 public:
@@ -50,11 +51,12 @@
 			double stopbandEdge,
 			double dBPassbandRipple,
 			double dBStopbandAtten,
-			uint16 samplingFreq);
+			uint32 samplingFreq,
+			uint16 upFactor);
 	
 	~FIRFilter() { free(_coeffs); }
 	
-	uint16 getLength() { return _length; }
+	uint32 getLength() { return _length; }
 	
 	double *getCoeffs() { return _coeffs; }
 
@@ -63,21 +65,22 @@
 			double dBPassbandRipple,
 			double dBStopbandAtten);
 	
-	uint16 windowLength(
+	uint32 windowLength(
 			double ripple,
 			double transitionBW,
-			uint16 samplingFreq);
+			uint32 samplingFreq,
+			uint16 upFactor);
 	
-	void windowDesign(double *coeffs, uint16 length, double ripple);
+	void windowDesign(double *coeffs, uint32 length, double ripple);
 	
 	double sinc(double arg);
 	
 	void LPDesign(
 			double *coeffs,
-			uint16 length,
+			uint32 length,
 			double passbandEdge,
 			double stopbandEdge,
-			uint16 samplingFreq);
+			uint32 samplingFreq);
 
 };
 

Modified: scummvm/branches/gsoc2007-mixer/sound/rate.cpp
===================================================================
--- scummvm/branches/gsoc2007-mixer/sound/rate.cpp	2007-08-14 08:49:20 UTC (rev 28611)
+++ scummvm/branches/gsoc2007-mixer/sound/rate.cpp	2007-08-14 12:42:56 UTC (rev 28612)
@@ -51,6 +51,7 @@
  */
 #define INTERMEDIATE_BUFFER_SIZE 512
 
+static st_rate_t gcd(st_rate_t a, st_rate_t b);
 
 /**
  * Audio rate converter based on simple resampling. Used when no
@@ -277,11 +278,8 @@
  * This currently allows us to upsample by an integer factor with very little
  * spectral distortion.
  *
- * TODO: Make this a rational factor rather than an integer factor
- *
- * Limited to sampling frequency <= 65535 Hz.
+ * TODO: Optimise filter creation -- too slow for large upsampling factors
  */
-
 template<bool stereo, bool reverseStereo>
 class FilteringRateConverter : public RateConverter {
 protected:
@@ -291,16 +289,22 @@
 	st_sample_t *inBuf;
 	
 	/* Offset into the circular buffer of the most recent input sample */
-	uint32 inPos;
+	uint16 inPos;
 	
+	/* 
+	 * The number of samples we need to fetch into the input buffer from the
+	 * AudioStream
+	 */
+	uint16 toFetch;
+	
 	/*
      * Fraction of the input frequency which should be used as passband for
 	 * the filter design.
 	 */
 	double lowpassBW;
 	
-	/* Number of filter banks to use for the multirate filter */
-	uint16 numBanks;
+	/* Upsampling and downsampling factors */
+	uint16 upFactor, downFactor;
 	
 	/* Subfilter length */
 	uint16 subLen;
@@ -339,36 +343,45 @@
  */
 template<bool stereo, bool reverseStereo>
 FilteringRateConverter<stereo, reverseStereo>::FilteringRateConverter(st_rate_t inrate, st_rate_t outrate) {
-	if (inrate >= 65536 || outrate >= 65536) {
-		error("rate effect can only handle rates < 65536");
-	}
+	/*
+	 * Note: we'll first upsample the audio by upFactor and filter it, and
+	 * then downsample it by downFactor.  This means we'll use upFactor
+	 * different filter banks and only calculate one in every downFactor
+	 * outputs.
+	 */
+	upFactor = outrate / gcd(inrate, outrate);
+	downFactor = inrate / gcd(inrate, outrate);
 	
-	currBank = 0;
+	/* Initially we'll need to put some samples in the input buffer */
+	toFetch = 1 + (downFactor - 1) / upFactor;
 	
-	numBanks = outrate / inrate;
+	/* We'll be ignoring the first downFactor - 1 samples */
+	currBank = (downFactor - 1) % upFactor;
 	
 	// TODO: Make an editable way to set this value
 	/* This sets the point in the input signal where attenuation will begin */
 	lowpassBW = 0.925;
 	
+	/* We'll want to lowpass based on the most restrictive frequency */
+	st_rate_t stopband = (outrate < inrate) ? outrate : inrate;
+	
 	// TODO: Make it so that this filter data can be reused by other
 	//       converters. 
 	/* Generate the filter coefficients */
-	filt = new FIRFilter(lowpassBW * inrate / 2.0, inrate / 2.0,
-						-40, 80, (uint16)outrate);
+	filt = new FIRFilter(lowpassBW * stopband / 2.0, stopband / 2.0,
+						-40, 80, (uint32)inrate * upFactor, upFactor);
 	
-	uint16 len = filt->getLength();
+	uint32 len = filt->getLength();
 	
-	subLen = (len + (numBanks - 1)) / numBanks;
+	subLen = (len + (upFactor - 1)) / upFactor;
 	
-	/* TODO: Fix this. */
-	/* At this point I don't have any code appending 0s in this case */
+	/* Want to be able to divide the filter perfectly into subfilters */
 	assert((len % subLen) == 0);
 	
 	/* Find the DC gain of each subfilter */
-	double *gain = (double *)calloc(numBanks, sizeof(double));
+	double *gain = (double *)calloc(upFactor, sizeof(double));
 	
-	uint16 i;
+	uint32 i;
 	for (i = 0; i < len; i++) {
 		/* 
 		 * Using the commented-out line and setting kFudgeFactor to 1 will
@@ -376,14 +389,14 @@
 		 * softer than those of other resamplers using otherwise equal
 		 * options.
 		 */
-		//gain[i % numBanks] += fabs((filt->getCoeffs())[i]);
-		gain[i % numBanks] += (filt->getCoeffs())[i];
+		//gain[i % upFactor] += fabs((filt->getCoeffs())[i]);
+		gain[i % upFactor] += (filt->getCoeffs())[i];
 	}
 	
 	/* Find the maximum of these subfilter gains */
 	filtGain = 0;
 	
-	for (i = 0; i < numBanks; i++) {
+	for (i = 0; i < upFactor; i++) {
 		if (gain[i] > filtGain) {
 			filtGain = gain[i];
 		}
@@ -408,7 +421,7 @@
 	st_sample_t *oend = obuf + osamp * 2;
 	
 	while (obuf < oend) {
-		if (currBank == 0) {
+		while (toFetch--) {
 			/*
 			 * We need to fetch a new input sample (two if this is a stereo
 			 * stream).  We'll want to replace the oldest sample(s) in the
@@ -442,10 +455,10 @@
 		 */
 		for (i = 0; i < subLen; i++) {
 			accum0 += (double)inBuf[(inPos + numChan * i) % subLen]
-						* (filt->getCoeffs())[currBank + i*numBanks];
+						* (filt->getCoeffs())[currBank + i*upFactor];
 			if (stereo) {
 				accum1 += (double)inBuf[(inPos + numChan * i + 1) % subLen]
-						* (filt->getCoeffs())[currBank + i*numBanks];
+						* (filt->getCoeffs())[currBank + i*upFactor];
 			}
 		}
 		
@@ -522,8 +535,11 @@
 		
 		obuf += 2;
 		
-		/* Circularly increment currBank */
-		currBank = (currBank + 1) % numBanks;
+		/* Find out how many more input samples are needed next time */
+		toFetch = (currBank + downFactor) / upFactor;
+		
+		/* Circularly increment currBank by downFactor */
+		currBank = (currBank + downFactor) % upFactor;
 	}
 	
 	return ST_SUCCESS;
@@ -597,13 +613,22 @@
 
 #pragma mark -
 
+static inline st_rate_t gcd(st_rate_t a, st_rate_t b) {
+	while (b) {
+		int temp = b;
+		b = a % b;
+		a = temp;
+	}
+	
+	return a;
+}
+
 // TODO: Add options checking for filtering rate converters.
 template<bool stereo, bool reverseStereo>
 RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate) {
 	if (ConfMan.hasKey("rate_converter")
 			&& (ConfMan.getInt("rate_converter") == kFilteringType)) {
-		/* Only handling integer upsampling rates at this point */
-		if (((outrate % inrate) == 0) && (outrate != inrate)) {
+		if (outrate != inrate) {
 			return new FilteringRateConverter<stereo, reverseStereo>(inrate, outrate);
 		}
 	}


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list