ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

ft2_windowed_sinc.c (4953B)


      1 // 8-point/16-point windowed-sinc interpolation LUT generator
      2 
      3 #include <stdint.h>
      4 #include <stdbool.h>
      5 #include <stdlib.h>
      6 #include <math.h>
      7 #include "ft2_windowed_sinc.h" // SINCx_WIDTH, SINCx_PHASES
      8 #include "../ft2_header.h" // PI
      9 #include "../ft2_video.h" // showErrorMsgBox()
     10 
     11 // globalized
     12 float *fSinc8_1 = NULL, *fSinc8_2 = NULL, *fSinc8_3 = NULL;
     13 float *fSinc16_1 = NULL, *fSinc16_2 = NULL, *fSinc16_3 = NULL;
     14 float *fSinc_1 = NULL, *fSinc_2 = NULL, *fSinc_3 = NULL;
     15 uint64_t sincRatio1, sincRatio2;
     16 
     17 // zeroth-order modified Bessel function of the first kind (series approximation)
     18 static inline double besselI0(double z)
     19 {
     20 #define EPSILON (1E-12) /* verified: lower than this makes no change when LUT output is single-precision float */
     21 
     22 	double s = 1.0, ds = 1.0, d = 2.0;
     23 	const double zz = z * z;
     24 
     25 	do
     26 	{
     27 		ds *= zz / (d * d);
     28 		s += ds;
     29 		d += 2.0;
     30 	}
     31 	while (ds > s*EPSILON);
     32 
     33 	return s;
     34 }
     35 
     36 static inline double sinc(double x)
     37 {
     38 	if (x == 0.0)
     39 	{
     40 		return 1.0;
     41 	}
     42 	else
     43 	{
     44 		x *= PI;
     45 		return sin(x) / x;
     46 	}
     47 }
     48 
     49 static inline double sincWithCutoff(double x, const double cutoff)
     50 {
     51 	if (x == 0.0)
     52 	{
     53 		return cutoff;
     54 	}
     55 	else
     56 	{
     57 		x *= PI;
     58 		return sin(cutoff * x) / x;
     59 	}
     60 }
     61 
     62 static void generateWindowedSinc(float *fOutput, const int32_t filterWidth, const int32_t filterPhases, const double beta, const double cutoff)
     63 {
     64 	const int32_t filterWidthBits = (int32_t)log2(filterWidth);
     65 	const int32_t filterWidthMask = filterWidth - 1;
     66 	const int32_t filterCenter = (filterWidth / 2) - 1;
     67 	const double besselI0Beta = 1.0 / besselI0(beta);
     68 	const double phaseMul = 1.0 / filterPhases;
     69 	const double xMul = 1.0 / (filterWidth / 2);
     70 
     71 	if (cutoff < 1.0)
     72 	{
     73 		// windowed-sinc with frequency cutoff
     74 		for (int32_t i = 0; i < filterPhases * filterWidth; i++)
     75 		{
     76 			const double x = ((i & filterWidthMask) - filterCenter) - ((i >> filterWidthBits) * phaseMul);
     77 
     78 			// Kaiser-Bessel window
     79 			const double n = x * xMul;
     80 			const double window = besselI0(beta * sqrt(1.0 - n * n)) * besselI0Beta;
     81 
     82 			fOutput[i] = (float)(sincWithCutoff(x, cutoff) * window);
     83 		}
     84 	}
     85 	else
     86 	{
     87 		// windowed-sinc with no frequency cutoff
     88 		for (int32_t i = 0; i < filterPhases * filterWidth; i++)
     89 		{
     90 			const double x = ((i & filterWidthMask) - filterCenter) - ((i >> filterWidthBits) * phaseMul);
     91 
     92 			// Kaiser-Bessel window
     93 			const double n = x * xMul;
     94 			const double window = besselI0(beta * sqrt(1.0 - n * n)) * besselI0Beta;
     95 
     96 			fOutput[i] = (float)(sinc(x) * window);
     97 		}
     98 	}
     99 }
    100 
    101 bool setupWindowedSincTables(void)
    102 {
    103 	fSinc8_1  = (float *)malloc(SINC1_WIDTH*SINC1_PHASES * sizeof (float));
    104 	fSinc8_2  = (float *)malloc(SINC1_WIDTH*SINC1_PHASES * sizeof (float));
    105 	fSinc8_3  = (float *)malloc(SINC1_WIDTH*SINC1_PHASES * sizeof (float));
    106 	fSinc16_1 = (float *)malloc(SINC2_WIDTH*SINC2_PHASES * sizeof (float));
    107 	fSinc16_2 = (float *)malloc(SINC2_WIDTH*SINC2_PHASES * sizeof (float));
    108 	fSinc16_3 = (float *)malloc(SINC2_WIDTH*SINC2_PHASES * sizeof (float));
    109 
    110 	if (fSinc8_1  == NULL || fSinc8_2  == NULL || fSinc8_3  == NULL ||
    111 		fSinc16_1 == NULL || fSinc16_2 == NULL || fSinc16_3 == NULL)
    112 	{
    113 		showErrorMsgBox("Not enough memory!");
    114 		return false;
    115 	}
    116 
    117 	// LUT-select resampling ratios
    118 	const double ratio1 = 1.1875; // fSinc_1 if <=
    119 	const double ratio2 = 1.5; // fSinc_2 if <=, fSinc_3 if >
    120 
    121 	// calculate mixer delta limits for LUT-selector
    122 	sincRatio1 = (uint64_t)(ratio1 * MIXER_FRAC_SCALE);
    123 	sincRatio2 = (uint64_t)(ratio2 * MIXER_FRAC_SCALE);
    124 
    125 	/* Kaiser-Bessel beta parameter
    126 	**
    127 	** Basically;
    128 	** Lower beta = less treble cut off, but more aliasing (shorter main lobe, more side lobe ripple)
    129 	** Higher beta = more treble cut off, but less aliasing (wider main lobe, less side lobe ripple)
    130 	**
    131 	** There simply isn't any optimal value here, it has to be tweaked to personal preference.
    132 	*/
    133 	const double b1 = 3.0 * M_PI; // alpha = 3.00 (beta = ~9.425)
    134 	const double b2 = 8.5;
    135 	const double b3 = 7.3;
    136 
    137 	// sinc low-pass cutoff (could maybe use some further tweaking)
    138 	const double c1 = 1.000;
    139 	const double c2 = 0.500;
    140 	const double c3 = 0.425;
    141 
    142 	// 8 point
    143 	generateWindowedSinc(fSinc8_1,  SINC1_WIDTH, SINC1_PHASES, b1, c1);
    144 	generateWindowedSinc(fSinc8_2,  SINC1_WIDTH, SINC1_PHASES, b2, c2);
    145 	generateWindowedSinc(fSinc8_3,  SINC1_WIDTH, SINC1_PHASES, b3, c3);
    146 
    147 	// 16 point
    148 	generateWindowedSinc(fSinc16_1, SINC2_WIDTH, SINC2_PHASES, b1, c1);
    149 	generateWindowedSinc(fSinc16_2, SINC2_WIDTH, SINC2_PHASES, b2, c2);
    150 	generateWindowedSinc(fSinc16_3, SINC2_WIDTH, SINC2_PHASES, b3, c3);
    151 
    152 	return true;
    153 }
    154 
    155 void freeWindowedSincTables(void)
    156 {
    157 	if (fSinc8_1 != NULL)
    158 	{
    159 		free(fSinc8_1);
    160 		fSinc8_1 = NULL;
    161 	}
    162 
    163 	if (fSinc8_2 != NULL)
    164 	{
    165 		free(fSinc8_2);
    166 		fSinc8_2 = NULL;
    167 	}
    168 
    169 	if (fSinc8_3 != NULL)
    170 	{
    171 		free(fSinc8_3);
    172 		fSinc8_3 = NULL;
    173 	}
    174 
    175 	if (fSinc16_1 != NULL)
    176 	{
    177 		free(fSinc16_1);
    178 		fSinc16_1 = NULL;
    179 	}
    180 
    181 	if (fSinc16_2 != NULL)
    182 	{
    183 		free(fSinc16_2);
    184 		fSinc16_2 = NULL;
    185 	}
    186 
    187 	if (fSinc16_3 != NULL)
    188 	{
    189 		free(fSinc16_3);
    190 		fSinc16_3 = NULL;
    191 	}
    192 }