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 }