Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 2 | /// |
michael@0 | 3 | /// FIR low-pass (anti-alias) filter with filter coefficient design routine and |
michael@0 | 4 | /// MMX optimization. |
michael@0 | 5 | /// |
michael@0 | 6 | /// Anti-alias filter is used to prevent folding of high frequencies when |
michael@0 | 7 | /// transposing the sample rate with interpolation. |
michael@0 | 8 | /// |
michael@0 | 9 | /// Author : Copyright (c) Olli Parviainen |
michael@0 | 10 | /// Author e-mail : oparviai 'at' iki.fi |
michael@0 | 11 | /// SoundTouch WWW: http://www.surina.net/soundtouch |
michael@0 | 12 | /// |
michael@0 | 13 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 14 | // |
michael@0 | 15 | // Last changed : $Date: 2014-01-05 15:40:22 -0600 (Sun, 05 Jan 2014) $ |
michael@0 | 16 | // File revision : $Revision: 4 $ |
michael@0 | 17 | // |
michael@0 | 18 | // $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $ |
michael@0 | 19 | // |
michael@0 | 20 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 21 | // |
michael@0 | 22 | // License : |
michael@0 | 23 | // |
michael@0 | 24 | // SoundTouch audio processing library |
michael@0 | 25 | // Copyright (c) Olli Parviainen |
michael@0 | 26 | // |
michael@0 | 27 | // This library is free software; you can redistribute it and/or |
michael@0 | 28 | // modify it under the terms of the GNU Lesser General Public |
michael@0 | 29 | // License as published by the Free Software Foundation; either |
michael@0 | 30 | // version 2.1 of the License, or (at your option) any later version. |
michael@0 | 31 | // |
michael@0 | 32 | // This library is distributed in the hope that it will be useful, |
michael@0 | 33 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
michael@0 | 34 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
michael@0 | 35 | // Lesser General Public License for more details. |
michael@0 | 36 | // |
michael@0 | 37 | // You should have received a copy of the GNU Lesser General Public |
michael@0 | 38 | // License along with this library; if not, write to the Free Software |
michael@0 | 39 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
michael@0 | 40 | // |
michael@0 | 41 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 42 | |
michael@0 | 43 | #include <memory.h> |
michael@0 | 44 | #include <assert.h> |
michael@0 | 45 | #include <math.h> |
michael@0 | 46 | #include <stdlib.h> |
michael@0 | 47 | #include "AAFilter.h" |
michael@0 | 48 | #include "FIRFilter.h" |
michael@0 | 49 | |
michael@0 | 50 | using namespace soundtouch; |
michael@0 | 51 | |
michael@0 | 52 | #define PI 3.141592655357989 |
michael@0 | 53 | #define TWOPI (2 * PI) |
michael@0 | 54 | |
michael@0 | 55 | // define this to save AA filter coefficients to a file |
michael@0 | 56 | // #define _DEBUG_SAVE_AAFILTER_COEFFICIENTS 1 |
michael@0 | 57 | |
michael@0 | 58 | #ifdef _DEBUG_SAVE_AAFILTER_COEFFICIENTS |
michael@0 | 59 | #include <stdio.h> |
michael@0 | 60 | |
michael@0 | 61 | static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len) |
michael@0 | 62 | { |
michael@0 | 63 | FILE *fptr = fopen("aa_filter_coeffs.txt", "wt"); |
michael@0 | 64 | if (fptr == NULL) return; |
michael@0 | 65 | |
michael@0 | 66 | for (int i = 0; i < len; i ++) |
michael@0 | 67 | { |
michael@0 | 68 | double temp = coeffs[i]; |
michael@0 | 69 | fprintf(fptr, "%lf\n", temp); |
michael@0 | 70 | } |
michael@0 | 71 | fclose(fptr); |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | #else |
michael@0 | 75 | #define _DEBUG_SAVE_AAFIR_COEFFS(x, y) |
michael@0 | 76 | #endif |
michael@0 | 77 | |
michael@0 | 78 | |
michael@0 | 79 | /***************************************************************************** |
michael@0 | 80 | * |
michael@0 | 81 | * Implementation of the class 'AAFilter' |
michael@0 | 82 | * |
michael@0 | 83 | *****************************************************************************/ |
michael@0 | 84 | |
michael@0 | 85 | AAFilter::AAFilter(uint len) |
michael@0 | 86 | { |
michael@0 | 87 | pFIR = FIRFilter::newInstance(); |
michael@0 | 88 | cutoffFreq = 0.5; |
michael@0 | 89 | setLength(len); |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | |
michael@0 | 93 | |
michael@0 | 94 | AAFilter::~AAFilter() |
michael@0 | 95 | { |
michael@0 | 96 | delete pFIR; |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | |
michael@0 | 100 | |
michael@0 | 101 | // Sets new anti-alias filter cut-off edge frequency, scaled to |
michael@0 | 102 | // sampling frequency (nyquist frequency = 0.5). |
michael@0 | 103 | // The filter will cut frequencies higher than the given frequency. |
michael@0 | 104 | void AAFilter::setCutoffFreq(double newCutoffFreq) |
michael@0 | 105 | { |
michael@0 | 106 | cutoffFreq = newCutoffFreq; |
michael@0 | 107 | calculateCoeffs(); |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | |
michael@0 | 111 | |
michael@0 | 112 | // Sets number of FIR filter taps |
michael@0 | 113 | void AAFilter::setLength(uint newLength) |
michael@0 | 114 | { |
michael@0 | 115 | length = newLength; |
michael@0 | 116 | calculateCoeffs(); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | |
michael@0 | 120 | |
michael@0 | 121 | // Calculates coefficients for a low-pass FIR filter using Hamming window |
michael@0 | 122 | void AAFilter::calculateCoeffs() |
michael@0 | 123 | { |
michael@0 | 124 | uint i; |
michael@0 | 125 | double cntTemp, temp, tempCoeff,h, w; |
michael@0 | 126 | double wc; |
michael@0 | 127 | double scaleCoeff, sum; |
michael@0 | 128 | double *work; |
michael@0 | 129 | SAMPLETYPE *coeffs; |
michael@0 | 130 | |
michael@0 | 131 | assert(length >= 2); |
michael@0 | 132 | assert(length % 4 == 0); |
michael@0 | 133 | assert(cutoffFreq >= 0); |
michael@0 | 134 | assert(cutoffFreq <= 0.5); |
michael@0 | 135 | |
michael@0 | 136 | work = new double[length]; |
michael@0 | 137 | coeffs = new SAMPLETYPE[length]; |
michael@0 | 138 | |
michael@0 | 139 | wc = 2.0 * PI * cutoffFreq; |
michael@0 | 140 | tempCoeff = TWOPI / (double)length; |
michael@0 | 141 | |
michael@0 | 142 | sum = 0; |
michael@0 | 143 | for (i = 0; i < length; i ++) |
michael@0 | 144 | { |
michael@0 | 145 | cntTemp = (double)i - (double)(length / 2); |
michael@0 | 146 | |
michael@0 | 147 | temp = cntTemp * wc; |
michael@0 | 148 | if (temp != 0) |
michael@0 | 149 | { |
michael@0 | 150 | h = sin(temp) / temp; // sinc function |
michael@0 | 151 | } |
michael@0 | 152 | else |
michael@0 | 153 | { |
michael@0 | 154 | h = 1.0; |
michael@0 | 155 | } |
michael@0 | 156 | w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window |
michael@0 | 157 | |
michael@0 | 158 | temp = w * h; |
michael@0 | 159 | work[i] = temp; |
michael@0 | 160 | |
michael@0 | 161 | // calc net sum of coefficients |
michael@0 | 162 | sum += temp; |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | // ensure the sum of coefficients is larger than zero |
michael@0 | 166 | assert(sum > 0); |
michael@0 | 167 | |
michael@0 | 168 | // ensure we've really designed a lowpass filter... |
michael@0 | 169 | assert(work[length/2] > 0); |
michael@0 | 170 | assert(work[length/2 + 1] > -1e-6); |
michael@0 | 171 | assert(work[length/2 - 1] > -1e-6); |
michael@0 | 172 | |
michael@0 | 173 | // Calculate a scaling coefficient in such a way that the result can be |
michael@0 | 174 | // divided by 16384 |
michael@0 | 175 | scaleCoeff = 16384.0f / sum; |
michael@0 | 176 | |
michael@0 | 177 | for (i = 0; i < length; i ++) |
michael@0 | 178 | { |
michael@0 | 179 | temp = work[i] * scaleCoeff; |
michael@0 | 180 | //#if SOUNDTOUCH_INTEGER_SAMPLES |
michael@0 | 181 | // scale & round to nearest integer |
michael@0 | 182 | temp += (temp >= 0) ? 0.5 : -0.5; |
michael@0 | 183 | // ensure no overfloods |
michael@0 | 184 | assert(temp >= -32768 && temp <= 32767); |
michael@0 | 185 | //#endif |
michael@0 | 186 | coeffs[i] = (SAMPLETYPE)temp; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | // Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384 |
michael@0 | 190 | pFIR->setCoefficients(coeffs, length, 14); |
michael@0 | 191 | |
michael@0 | 192 | _DEBUG_SAVE_AAFIR_COEFFS(coeffs, length); |
michael@0 | 193 | |
michael@0 | 194 | delete[] work; |
michael@0 | 195 | delete[] coeffs; |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | |
michael@0 | 199 | // Applies the filter to the given sequence of samples. |
michael@0 | 200 | // Note : The amount of outputted samples is by value of 'filter length' |
michael@0 | 201 | // smaller than the amount of input samples. |
michael@0 | 202 | uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const |
michael@0 | 203 | { |
michael@0 | 204 | return pFIR->evaluate(dest, src, numSamples, numChannels); |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | |
michael@0 | 208 | /// Applies the filter to the given src & dest pipes, so that processed amount of |
michael@0 | 209 | /// samples get removed from src, and produced amount added to dest |
michael@0 | 210 | /// Note : The amount of outputted samples is by value of 'filter length' |
michael@0 | 211 | /// smaller than the amount of input samples. |
michael@0 | 212 | uint AAFilter::evaluate(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) const |
michael@0 | 213 | { |
michael@0 | 214 | SAMPLETYPE *pdest; |
michael@0 | 215 | const SAMPLETYPE *psrc; |
michael@0 | 216 | uint numSrcSamples; |
michael@0 | 217 | uint result; |
michael@0 | 218 | int numChannels = src.getChannels(); |
michael@0 | 219 | |
michael@0 | 220 | assert(numChannels == dest.getChannels()); |
michael@0 | 221 | |
michael@0 | 222 | numSrcSamples = src.numSamples(); |
michael@0 | 223 | psrc = src.ptrBegin(); |
michael@0 | 224 | pdest = dest.ptrEnd(numSrcSamples); |
michael@0 | 225 | result = pFIR->evaluate(pdest, psrc, numSrcSamples, numChannels); |
michael@0 | 226 | src.receiveSamples(result); |
michael@0 | 227 | dest.putSamples(result); |
michael@0 | 228 | |
michael@0 | 229 | return result; |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | |
michael@0 | 233 | uint AAFilter::getLength() const |
michael@0 | 234 | { |
michael@0 | 235 | return pFIR->getLength(); |
michael@0 | 236 | } |