Exemplo n.º 1
0
 private Deemphasis(int length, double sampleRate, double binSize, bool correctPhase, bool doAnalysis)
 {
     this.length               = length;
     this.sampleRate           = sampleRate;
     this.binSize              = binSize;
     this.correctPhase         = correctPhase;
     this.filterKernel         = null;
     this.idealFilterResponse  = null;
     this.doAnalysis           = doAnalysis;
     this.actualFilterResponse = null;
     this.deviation            = null;
 }
Exemplo n.º 2
0
        ////////////////////////////////////////////////////////////////////////////////
        //
        //  Function:       calculateAmplitudeAndPhase
        //
        //  Arguments:      response:     Pointer to the response structure.
        //                  R1:           The resister 1 value in Ohms.
        //                  Rf:           The feedback resistor value in Ohms.
        //                  C1:           The capacitor value in Farads.
        //
        //  Returns:        void
        //
        //  Description:    The function calculates the amplitude response (both linear
        //                  and dB) of the ideal de-emphasis filter. It also calculates
        //                  the phase response (in radians), phase delay (in seconds),
        //                  and group delay (in seconds) of the ideal filter. Note that
        //                  this function assumes that the frequency and complex
        //                  response vectors have already been calculated.
        //
        ////////////////////////////////////////////////////////////////////////////////

        private static void calculateAmplitudeAndPhase(IdealResponse response, double R1, double Rf,
                                                       double C1)
        {
            // Make sure we have a valid pointer
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            // Calculate the magnitude of the frequency response
            for (int i = 0; i < response.complexResponse.Length; i++)
            {
                // Isolate the real and imaginary parts of the complex response
                double a = response.complexResponse[i].Real;
                double b = response.complexResponse[i].Imaginary;

                // Calculate the linear amplitude
                response.amplitudeLinear[i] = Math.Sqrt((a * a) + (b * b));

                // Calculate the dB-scaled amplitude from the linear amplitude
                response.amplitudeDB[i] =
                    FourierAnalysis.convertToDB(response.amplitudeLinear[i]);

                // Calculate the phase response (in radians)
                response.phaseRadians[i] = Math.Atan(b / a);

                // Calculate the phase delay (in seconds)
                if (response.frequency[i] == 0.0)
                {
                    // The phase delay at f = 0 is a special case: it is C1 * Rf
                    response.phaseDelay[i] = C1 * Rf;
                }
                else
                {
                    // The phase delay is phi(f) = - phase(f) / (2 * Pi * f)
                    response.phaseDelay[i] =
                        response.phaseRadians[i] /
                        (-Constants.TAU * response.frequency[i]);
                }

                // Calculate the group delay (in seconds)
                response.groupDelay[i] =
                    groupDelayFunc(response.frequency[i], R1, Rf, C1);
            }
        }
Exemplo n.º 3
0
        ////////////////////////////////////////////////////////////////////////////////
        //
        //  Function:       calculateIdealResponse
        //
        //  Arguments:      length:         Filter kernel length.
        //                  sampleRate:     System sample rate in Hertz. Must
        //									be greater than 0.
        //
        //  Returns:        A structure that holds the ideal frequency response
        //                  of the de-emphasis filter.
        //
        //  Description:    This function calculates the ideal frequency response of
        //                  the canonical de-emphasis filter needed to correct the pre-
        //                  emphasis used on older compact discs (CDs). It does so by
        //                  inverting the reponse of the pre-emphasis filter. This
        //                  pre-emphasis filter is normally implemented using an op-amp
        //                  in a "non-inverting high-pass shelving amplifier" topology.
        //                  The input voltage is fed into the +input of the op-amp,
        //                  and the output is fed back into the -input through a
        //                  feedback resistor (Rf). This input is also grounded through
        //                  an R1 resistor and C1 capacitor connected in series.
        //
        //                              |\
        //                              | \
        //                              |  \
        //                  Vi ---------|+  \
        //                              |    \
        //                              |     >------.------- Vo
        //                              |    /       |
        //                          .---|-  /        |
        //                          |   |  /         |
        //                          |   | /          |
        //                          |   |/           |
        //                          |        Rf      |
        //                          .------/\/\/-----.
        //                          |
        //                          \
        //                          /
        //                          \ R1
        //                          /
        //                          |
        //                          = C1
        //                          |
        //                          |
        //                         GND
        //
        //                  Rf = 35000 Ohms
        //                  R1 = 15000 Ohms
        //                  C1 = 0.000000001 Farads (1 nF)
        //
        ////////////////////////////////////////////////////////////////////////////////

        public static IdealResponse calculateIdealResponse(int length, double sampleRate)
        {
            // Make sure the length of the filter is at least 8 samples
            if (length < 8)
            {
                throw new ArgumentOutOfRangeException("length", "" + length);
            }

            // Make sure the length of the filter is a power of 2
            if (!Utility.isPowerOf2(length))
            {
                throw new ArgumentOutOfRangeException("length", "" + length);
            }

            // Make sure the sample rate is greater than 0
            if (sampleRate <= 0)
            {
                throw new ArgumentOutOfRangeException("sampleRate", "" + sampleRate);
            }

            // Define constants used for the resistors and capacitor in the original
            // op-amp filter design
            const double R1 = 15000.0;      // Ohms
            const double Rf = 35000.0;      // Ohms
            const double C1 = 0.000000001;  // Farads

            // Calculate the nyquist frequency
            double nyquist = sampleRate / 2.0;

            // Calculate the center index
            int centerIndex = (length / 2);

            // Calculate the offset
            int offset = centerIndex - 1;

            // Calculate the bin size. This is the difference between the frequencies
            // of consecutive vector elements.
            double binSize = nyquist / ((double)centerIndex);

            // Create a structure to hold the desired frequency response
            IdealResponse response = new IdealResponse(length);

            // Set the sample rate and bin size
            response.sampleRate = sampleRate;
            response.binSize    = binSize;

            // Calculate the complex frequency response in rectangular form
            for (int i = 0; i < length; i++)
            {
                // Calculate the frequency in Hz
                double f = sampleRate * ((double)(i - offset) / (double)length);

                // Record this in the frequency vector
                response.frequency[i] = f;

                // Calculate the complex response of the ideal de-emphasis filter
                // at this frequency
                response.complexResponse[i] =
                    calculateComplexResponse(f, R1, Rf, C1);
            }

            // Calculate the amplitude and phase responses from the complex response
            calculateAmplitudeAndPhase(response, R1, Rf, C1);

            // Return the filled-in structure
            return(response);
        }
Exemplo n.º 4
0
        ////////////////////////////////////////////////////////////////////////////////
        //
        //  Function:       createDeemphasisFilter
        //
        //  Arguments:      length:         Filter kernel length.
        //                  sampleRate:     System sample rate in Hertz. Must
        //									be greater than 0.
        //                  doAnalysis:     If set to true, this function will
        //                                  calculate and print the filter response.
        //                  correctPhase:   If set to true, this function corrects the
        //                                  phase distortion produced by the pre-
        //                                  emphasis filter.
        //
        //  Returns:        Returns a pointer to a struct containing pointer to the
        //					filter kernel plus other information
        //
        //  Description:    This function creates a filter kernel that implements
        //                  the de-emphasis filter needed to compensate for the pre-
        //                  emphasis filtering that is present on some CD recordings.
        //                  The filter will correct the frequency response of the input
        //                  to produce an output that should have a spectrum identical
        //                  to the original recording before an emphasis filter was
        //                  applied. If the correctPhase switch is set to true, then
        //                  the phase is also corrected; if not, a linear phase filter
        //                  is created.
        //
        ////////////////////////////////////////////////////////////////////////////////

        public static Deemphasis createDeemphasisFilter(int length, double sampleRate,
                                                        bool correctPhase, bool doAnalysis)
        {
            // Make sure the length of the filter is at least 8 samples
            if (length < 8)
            {
                throw new ArgumentOutOfRangeException("length", "" + length);
            }

            // Make sure the length of the filter is power of 2
            if (!Utility.isPowerOf2(length))
            {
                throw new ArgumentOutOfRangeException("length", "" + length);
            }

            // Make sure the sample rate is greater than 0
            if (sampleRate <= 0)
            {
                throw new ArgumentOutOfRangeException("sampleRate", "" + sampleRate);
            }

            // Calculate the bin size
            double binSize = sampleRate / (double)length;

            // Make sure the binsize is > 0 and <= 1/4 of the sample rate. This
            // guarantees at least 3 data points when calculating the filter
            // response, i.e. at DC, nyquist/2, and nyquist.
            if ((binSize <= 0) || (binSize > sampleRate / 4))
            {
                throw new ArgumentOutOfRangeException("binSize", "" + binSize);
            }

            // Allocate memory for the deemphasis filter struct
            Deemphasis deemphasis = new Deemphasis(length, sampleRate, binSize,
                                                   correctPhase, doAnalysis);

            // Calculate the ideal frequency response of the de-emphasis filter
            deemphasis.idealFilterResponse =
                IdealResponse.calculateIdealResponse(length, sampleRate);

            // Create the de-emphasis filter kernel, using the ideal filter response
            deemphasis.filterKernel =
                createFilterKernel(deemphasis.idealFilterResponse,
                                   deemphasis.correctPhase);

            // If we are doing analysis, then calculate the response and deviation
            // of this filter kernel, and print out the results
            if (deemphasis.doAnalysis)
            {
                // Calculate the actual filter response
                //deemphasis.actualFilterResponse =
                //    IdealResponse.calculateIdealResponse(deemphasis.filterKernel,
                //                            sampleRate, binSize);

                // Calculate and print out deviations for
                // amplitude (dB), phase Delay (s), group delay (s)
                //deemphasis.deviation = Deviation.calculateDeviation(deemphasis);
            }

            // Return a pointer to the newly created deemphasis struct
            return(deemphasis);
        }
Exemplo n.º 5
0
        ////////////////////////////////////////////////////////////////////////////////
        //
        //  Function:       createFilterKernel
        //
        //  Arguments:      idealResponse:     Pointer to the ideal filter response.
        //
        //  Returns:        A pointer to the newly-created filter kernel.
        //
        //  Description:    This function creates a real-valued filter kernel whose
        //                  response closely matches the response of the ideal
        //                  de-emphasis filter, including phase correction.
        //
        ////////////////////////////////////////////////////////////////////////////////

        private static double[] createFilterKernel(IdealResponse idealResponse,
                                                   bool correctPhase)
        {
            int    i, j;
            double a, b, magnitude;


            // Make sure we have a valid pointer
            if (idealResponse == null)
            {
                throw new ArgumentNullException("idealResponse");
            }

            // Create a complex vector to hold the rotated complex filter response
            Complex[] complexFilterKernel = new Complex[idealResponse.complexResponse.Length];

            // Calculate the number of (positive or negative) harmonics. Since the
            // complex response contains both positive and negative frequencies, this
            // will be 1/2 its length.
            int numberHarmonics = complexFilterKernel.Length / 2;

            // Calculate the position of the DC component
            int DCIndex = numberHarmonics - 1;

            // Rotate and copy the complex response into the vector. We rotate so that
            // the harmonics are ordered as follows:
            //    0 (DC), 1, 2, 3, ..., N-1, N (nyquist), -(N-1), ..., -3, -2, -1
            // First copy from the harmonics ranging from 0 (DC) to N (nyquist)
            // into the vector
            for (i = 0, j = DCIndex; j < complexFilterKernel.Length; i++, j++)
            {
                complexFilterKernel[i] =
                    idealResponse.complexResponse[j];
            }

            // Make note of the position of the sample at the Nyquist frequency
            int nyquistPosition = i - 1;

            // And then copy the harmonics ranging from -(N-1) to -1 into the vector.
            // Note that we assume the value of i is carried over from previous loop.
            for (j = 0; j < DCIndex; i++, j++)
            {
                complexFilterKernel[i] =
                    idealResponse.complexResponse[j];
            }

            // If we are NOT doing phase correction, then we are creating a filter
            // with linear phase. We must use the magnitude for the real part of the
            // complex response, and set the imaginary part to zero.
            if (correctPhase == false)
            {
                for (i = 0; i < complexFilterKernel.Length; i++)
                {
                    a         = complexFilterKernel[i].Real;
                    b         = complexFilterKernel[i].Imaginary;
                    magnitude = Math.Sqrt((a * a) + (b * b));
                    complexFilterKernel[i] = magnitude + Complex.ImaginaryOne * 0;
                }
            }

            // Correct the complex filter kernel by setting the phase at the nyquist
            // frequency to zero. The imaginary part is zeroed, and the real part
            // is given the full magnitude of the original complex value.
            a         = complexFilterKernel[nyquistPosition].Real;
            b         = complexFilterKernel[nyquistPosition].Imaginary;
            magnitude = Math.Sqrt((a * a) + (b * b));
            complexFilterKernel[nyquistPosition] = magnitude + Complex.ImaginaryOne * 0;

            // Perform a complex scaled in-place IFFT on the complex response. The
            // result is a complex vector, where all the imaginary components should be
            // 0.0. Note that the IFFT gives imaginary components very close to 0.0,
            // while the IDFT gives a noisier result.
            FourierAnalysis.complexScaledIFFT(complexFilterKernel);

            // Copy the real part of the complex vector into a newly-created real vector
            double[] temp = new double[complexFilterKernel.Length];
            for (i = 0; i < temp.Length; i++)
            {
                temp[i] = complexFilterKernel[i].Real;
            }

            // Rotate the vector by N/2 to create the unwindowed filter kernel
            double[] kernel   = new double[temp.Length];
            int      midpoint = temp.Length / 2;

            for (i = 0, j = midpoint; j < temp.Length; i++, j++)
            {
                kernel[i] = temp[j];
            }
            for (j = 0; j < midpoint; i++, j++)
            {
                kernel[i] = temp[j];
            }

            // Apply the Blackman-Harris window to the kernel, to reduce aliasing
            // which shows up as ripples in the filter's response
            applyHarrisWindow(kernel);

            // Return a pointer to the newly-calculated filter kernel
            return(kernel);
        }