Began implementing IIR and FIR filters, and got zero stuffing and sample discarding working. gsoc2008_audio_resampling
authorAaron Wishnick <schnarf@gmail.com>
Wed, 18 Jun 2008 18:55:50 +0000
branchgsoc2008_audio_resampling
changeset 2656dd74182b3c3c
parent 2655 b8e736c8a5a8
child 2657 29306e52dab8
Began implementing IIR and FIR filters, and got zero stuffing and sample discarding working.
include/SDL_audio.h
src/audio/SDL_audiocvt.c
     1.1 --- a/include/SDL_audio.h	Wed Jun 18 04:51:10 2008 +0000
     1.2 +++ b/include/SDL_audio.h	Wed Jun 18 18:55:50 2008 +0000
     1.3 @@ -142,8 +142,8 @@
     1.4      SDL_AudioFormat dst_format; /* Target audio format */
     1.5      double rate_incr;           /* Rate conversion increment */
     1.6      Uint8 *buf;                 /* Buffer to hold entire audio data */
     1.7 -	Uint8 *sinc;				/* Windowed sinc filter */
     1.8 -	Uint8 *state_buf;			/* Sample history for either the FIR or IIR filter */
     1.9 +	Uint8 *coeff;				/* Filter coefficients: either big windowed sinc filter, or 6 IIR lowpass coefficients*/
    1.10 +	Uint8 *state_buf;			/* Sample history for either the FIR or IIR filter. For IIR filter, first two elements are X, second two are Y, and state_pos toggles the order */
    1.11  	int state_pos;				/* Position in the state */
    1.12  	int len_sinc;				/* Length of windowed sinc filter, in appropriate units (not necessarily bytes) */
    1.13      int len;                    /* Length of original audio buffer */
     2.1 --- a/src/audio/SDL_audiocvt.c	Wed Jun 18 04:51:10 2008 +0000
     2.2 +++ b/src/audio/SDL_audiocvt.c	Wed Jun 18 18:55:50 2008 +0000
     2.3 @@ -27,6 +27,8 @@
     2.4  #include "SDL_audio.h"
     2.5  #include "SDL_audio_c.h"
     2.6  
     2.7 +#define DEBUG_CONVERT
     2.8 +
     2.9  /* Effectively mix right and left channels into a single channel */
    2.10  static void SDLCALL
    2.11  SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    2.12 @@ -1237,7 +1239,7 @@
    2.13      int i, j;
    2.14  
    2.15  #ifdef DEBUG_CONVERT
    2.16 -    fprintf(stderr, "Converting audio rate via proper resampling (mono)\n");
    2.17 +    printf("Converting audio rate via proper resampling (mono)\n");
    2.18  #endif
    2.19  
    2.20  #define zerostuff_mono(type) { \
    2.21 @@ -1256,7 +1258,7 @@
    2.22  #define discard_mono(type) { \
    2.23          const type *src = (const type *) (cvt->buf); \
    2.24          type *dst = (type *) (cvt->buf); \
    2.25 -        for (i = 0; i < cvt->len_cvt / sizeof (type); ++i) { \
    2.26 +        for (i = 0; i < cvt->len_cvt / cvt->len_div / sizeof (type); ++i) { \
    2.27              dst[0] = src[0]; \
    2.28              src += cvt->len_div; \
    2.29              ++dst; \
    2.30 @@ -1264,6 +1266,9 @@
    2.31      }
    2.32  
    2.33  	// Step 1: Zero stuff the conversion buffer
    2.34 +#ifdef DEBUG_CONVERT
    2.35 +	printf("Zero-stuffing by a factor of %u\n", cvt->len_mult);
    2.36 +#endif
    2.37      switch (SDL_AUDIO_BITSIZE(format)) {
    2.38      case 8:
    2.39          zerostuff_mono(Uint8);
    2.40 @@ -1281,6 +1286,9 @@
    2.41  	// Step 2: Use either a windowed sinc FIR filter or IIR lowpass filter to remove all alias frequencies
    2.42  	
    2.43  	// Step 3: Discard unnecessary samples
    2.44 +#ifdef DEBUG_CONVERT
    2.45 +	printf("Discarding samples by a factor of %u\n", cvt->len_div);
    2.46 +#endif
    2.47      switch (SDL_AUDIO_BITSIZE(format)) {
    2.48      case 8:
    2.49          discard_mono(Uint8);
    2.50 @@ -1418,13 +1426,53 @@
    2.51  	coeff[4] = -2.0f * cosw0 * scale;
    2.52  	coeff[5] = (1.0f - alpha) * scale;
    2.53  	
    2.54 -	/* Convert coefficients to fixed point, using the range (-2.0, 2.0) */
    2.55 +	/* Copy the coefficients to the struct. If necessary, convert coefficients to fixed point, using the range (-2.0, 2.0) */
    2.56 +	if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
    2.57 +		float *cvt_coeff = (float *)cvt->coeff;
    2.58 +		int i;
    2.59 +		for(i = 0; i < 6; ++i) {
    2.60 +			cvt_coeff[i] = coeff[i];
    2.61 +		}
    2.62 +	} else {
    2.63 +	}
    2.64  	
    2.65  	/* Initialize the state buffer to all zeroes, and set initial position */
    2.66  	memset(cvt->state_buf, 0, 4 * SDL_AUDIO_BITSIZE(format) / 4);
    2.67  	cvt->state_pos = 0;
    2.68  }
    2.69  
    2.70 +/* Apply the lowpass IIR filter to the given SDL_AudioCVT struct */
    2.71 +int SDL_FilterIIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) {
    2.72 +	int i, n;
    2.73 +	
    2.74 +	n = cvt->len_cvt / (SDL_AUDIO_BITSIZE(format) / 4);
    2.75 +	
    2.76 +	if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
    2.77 +		float *coeff = (float *)cvt->coeff;
    2.78 +		float *state = (float *)cvt->state_buf;
    2.79 +		float *buf = (float *)cvt->buf;
    2.80 +		float temp;
    2.81 +
    2.82 +		
    2.83 +		for(i = 0; i < n; ++i) {
    2.84 +			/* y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a[2] * y[n-2] */
    2.85 +			temp = buf[n];
    2.86 +			if( cvt->state_pos ) {
    2.87 +				buf[n] = coeff[0] * buf[n] + coeff[1] * state[0] + coeff[2] * state[1] - coeff[4] * state[2] - coeff[5] * state[3];
    2.88 +				state[1] = temp;
    2.89 +				state[3] = buf[n];
    2.90 +				cvt->state_pos = 0;
    2.91 +			} else {
    2.92 +				buf[n] = coeff[0] * buf[n] + coeff[1] * state[1] + coeff[2] * state[0] - coeff[4] * state[3] - coeff[5] * state[2];
    2.93 +				state[0] = temp;
    2.94 +				state[2] = buf[n];
    2.95 +				cvt->state_pos = 1;
    2.96 +			}
    2.97 +		}
    2.98 +	} else {
    2.99 +	}
   2.100 +}
   2.101 +
   2.102  /* Apply the windowed sinc FIR filter to the given SDL_AudioCVT struct */
   2.103  int SDL_FilterFIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) {
   2.104  	int n = cvt->len_cvt / (SDL_AUDIO_BITSIZE(format) / 4);
   2.105 @@ -1437,7 +1485,7 @@
   2.106  	   significantly fewer multiplications and additions.
   2.107  	*/
   2.108  #define filter_sinc(type, shift_bits) { \
   2.109 -			type *sinc = (type *)cvt->sinc; \
   2.110 +			type *sinc = (type *)cvt->coeff; \
   2.111  			type *state = (type *)cvt->state_buf; \
   2.112  			type *buf = (type *)cvt->buf; \
   2.113  			for(i = 0; i < n; ++i) { \
   2.114 @@ -1449,17 +1497,33 @@
   2.115  				} \
   2.116  			} \
   2.117  		}
   2.118 -			
   2.119 -	switch (SDL_AUDIO_BITSIZE(format)) {
   2.120 -		case 8:
   2.121 -			filter_sinc(Uint8, 4);
   2.122 -			break;
   2.123 -		case 16:
   2.124 -			filter_sinc(Uint16, 8);
   2.125 -			break;
   2.126 -		case 32:
   2.127 -			filter_sinc(Uint32, 16);
   2.128 -			break;
   2.129 +	
   2.130 +	/* If it's floating point, we don't need to do any shifting */
   2.131 +	if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
   2.132 +		float *sinc = (float *)cvt->coeff;
   2.133 +		float *state = (float *)cvt->state_buf;
   2.134 +		float *buf = (float *)cvt->buf;
   2.135 +		
   2.136 +		for(i = 0; i < n; ++i) {
   2.137 +			state[cvt->state_pos++] = buf[i];
   2.138 +			if(cvt->state_pos == m) cvt->state_pos = 0;
   2.139 +			buf[i] = 0.0f;
   2.140 +			for(j = 0; j < m; ++j) {
   2.141 +				buf[i] += state[j] * sinc[j];
   2.142 +			}
   2.143 +		}
   2.144 +	} else {
   2.145 +		switch (SDL_AUDIO_BITSIZE(format)) {
   2.146 +			case 8:
   2.147 +				filter_sinc(Uint8, 4);
   2.148 +				break;
   2.149 +			case 16:
   2.150 +				filter_sinc(Uint16, 8);
   2.151 +				break;
   2.152 +			case 32:
   2.153 +				filter_sinc(Uint32, 16);
   2.154 +				break;
   2.155 +		}
   2.156  	}
   2.157  	
   2.158  #undef filter_sinc
   2.159 @@ -1482,7 +1546,7 @@
   2.160  	unsigned int i;
   2.161  
   2.162  	/* Check that the buffer is allocated */
   2.163 -	if( cvt->sinc == NULL ) {
   2.164 +	if( cvt->coeff == NULL ) {
   2.165  		return -1;
   2.166  	}
   2.167  
   2.168 @@ -1519,7 +1583,7 @@
   2.169  	
   2.170  #define convert_fixed(type, size) { \
   2.171  		norm_fact = size / norm_sum; \
   2.172 -		type *dst = (type *)cvt->sinc; \
   2.173 +		type *dst = (type *)cvt->coeff; \
   2.174  		for( i = 0; i <= m; ++i ) { \
   2.175  			dst[i] = (type)(fSinc[i] * norm_fact); \
   2.176  		} \
   2.177 @@ -1527,7 +1591,7 @@
   2.178  	
   2.179  	/* If we're using floating point, we only need to normalize */
   2.180  	if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
   2.181 -		float *fDest = (float *)cvt->sinc;
   2.182 +		float *fDest = (float *)cvt->coeff;
   2.183  		norm_fact = 1.0f / norm_sum;
   2.184  		for(i = 0; i <= m; ++i) {
   2.185  			fDest[i] = fSinc[i] * norm_fact;
   2.186 @@ -1555,6 +1619,17 @@
   2.187  	free(fSinc);
   2.188  }
   2.189  
   2.190 +/* This is used to reduce the resampling ratio */
   2.191 +inline int SDL_GCD(int a, int b) {
   2.192 +	int temp;
   2.193 +	while(b != 0) {
   2.194 +		temp = a % b;
   2.195 +		a = b;
   2.196 +		b = temp;
   2.197 +	}
   2.198 +	return a;
   2.199 +}
   2.200 +
   2.201  
   2.202  /* Creates a set of audio filters to convert from one format to another.
   2.203     Returns -1 if the format conversion is not supported, 0 if there's
   2.204 @@ -1644,7 +1719,14 @@
   2.205      }
   2.206  
   2.207      /* Do rate conversion */
   2.208 -    cvt->rate_incr = 0.0;
   2.209 +	int rate_gcd;
   2.210 +	rate_gcd = SDL_GCD(src_rate, dst_rate);
   2.211 +	cvt->len_mult = dst_rate / rate_gcd;
   2.212 +	cvt->len_div = src_rate / rate_gcd;
   2.213 +	cvt->len_ratio = (double)cvt->len_mult / (double)cvt->len_div;
   2.214 +	cvt->filters[cvt->filter_index++] = SDL_Resample;
   2.215 +	
   2.216 +    /*cvt->rate_incr = 0.0;
   2.217      if ((src_rate / 100) != (dst_rate / 100)) {
   2.218          Uint32 hi_rate, lo_rate;
   2.219          int len_mult;
   2.220 @@ -1693,16 +1775,16 @@
   2.221              }
   2.222              len_mult = 2;
   2.223              len_ratio = 2.0;
   2.224 -        }
   2.225 +        }*/
   2.226          /* If hi_rate = lo_rate*2^x then conversion is easy */
   2.227 -        while (((lo_rate * 2) / 100) <= (hi_rate / 100)) {
   2.228 +        /*while (((lo_rate * 2) / 100) <= (hi_rate / 100)) {
   2.229              cvt->filters[cvt->filter_index++] = rate_cvt;
   2.230              cvt->len_mult *= len_mult;
   2.231              lo_rate *= 2;
   2.232              cvt->len_ratio *= len_ratio;
   2.233 -        }
   2.234 +        }*/
   2.235          /* We may need a slow conversion here to finish up */
   2.236 -        if ((lo_rate / 100) != (hi_rate / 100)) {
   2.237 +        /*if ((lo_rate / 100) != (hi_rate / 100)) {*/
   2.238  #if 1
   2.239              /* The problem with this is that if the input buffer is
   2.240                 say 1K, and the conversion rate is say 1.1, then the
   2.241 @@ -1722,8 +1804,8 @@
   2.242              }
   2.243              cvt->filters[cvt->filter_index++] = SDL_RateSLOW;
   2.244  #endif
   2.245 -        }
   2.246 -    }
   2.247 +/*        }
   2.248 +    }*/
   2.249  
   2.250      /* Set up the filter information */
   2.251      if (cvt->filter_index != 0) {