Finite Impulse Response Filter


REMEZ

REMEZ is a library that allows designing: Low Pass, Band Pass, High Pass, Hilbert and Differentiator filters. The REMEZ library is based on the Parks-McClellan algorithm, see Discrete-Time Signal Processing (Alan V. Oppenheim and Ronal W. Schafer).
REMEZ es una librería que permite diseñar filtros: Pasa Baja, Pasa Banda, Pasa Alta, de Hilbert y Diferenciadores. La librería de REMEZ está basa en el algoritmo de Parks-McClellan, vea Discrete-Time Signal Processing (Alan V. Oppenheim and Ronal W. Schafer).

Problem 1
Create a project called RemezTest to create a Low Pass filter using the REMEZ algorithm. The program must display the Impulse and Frequency Response. Use a slider to control the length of the filter. The filter has a cut frequency of 2.1 radians. Additionally, the filter must eliminate any frequency greater than 2.6 radians.
Cree un proyecto llamado RemezTest para crear un filtro pasa bajas usando el algoritmo de REMEZ. El progrma debe mostrar la respuesta al impulso y la respuesta en frecuencia. Use un slider para la longitud del filtro. El filtro tiene una frecuencia de corte de 2.1 radianes. Adicionalmente, el filtro debe eliminar cualquier frecuencia mayor a 2.6 radianes.

Solution A
Create a Wintempla Dialog Application. Using Wintempla create the GUI shown below. After inserting the slider for the length of the filter, double click the control and mark the HSCROLL event.
Cree una aplicación de Diálogo de Wintempla. Use Wintempla para la crear la GUI mostrada debajo. Después de insertar el slider para la longitud del filtro, haga doble clic en el control para marcar el evento de HSCROLL.

RemezTestGui

Solution B
Edit the RemezTest.h file as shown.
Edite el archivo RemezTest.h como se muestra.

RemezTest.h
#pragma once //______________________________________ RemezTest.h
#include "resource.h"

class RemezTest: public Win::Dialog
{
public:
     RemezTest()
     {
     }
     ~RemezTest()
     {
     }
     void UpdateGraphs();
protected:
     ...
};

Solution C
Edit the RemezTest.cpp file as shown.
Edite el archivo RemezTest.cpp como se muestra.

RemezTest.Cpp
...

void RemezTest::Window_Open(Win::Event& e)
{
     //________________________________________________________ sldLength
     sldLength.SetRange(2, 200);
     sldLength.Position = 10;
     tbxLength.IntValue = 10;
     //________________________________________________________ xyFreqResp
     xyFreqResp.CaptionX = L"Frequency";
     xyFreqResp.CaptionY = L"Amplitude";
     xyFreqResp.MinX= 0.0;
     xyFreqResp.MaxX= M_PI;
     xyFreqResp.MinY= 0.0;
     xyFreqResp.MaxY= 1.2;
     xyFreqResp.DivisionCountY = 6;
     xyFreqResp.Graphs.Add();

     //________________________________________________________ xyImpulseResponse
     xyImpulseResponse.CaptionX = L"n";
     xyImpulseResponse.CaptionY = L"h[n]";
     xyImpulseResponse.MinX= -1.0;
     xyImpulseResponse.MaxX= 6.28;
     xyImpulseResponse.MinY= -0.2;
     xyImpulseResponse.MaxY= 0.8;
     xyImpulseResponse.DivisionCountY = 5;
     xyImpulseResponse.Graphs.Add();
     xyImpulseResponse.Graphs[0].Type = Win::Graph::impulse;

     //________________________________________________________ xyError
     //xyError.CaptionX = L"Frequency";
     //xyError.CaptionY = L"Error";
     //xyError.MinX= 0.0;
     //xyError.MaxX= M_PI;
     //xyError.MinY= -0.2;
     //xyError.MaxY= 0.2;
     //xyError.Graphs.Add();
     //
     UpdateGraphs();
}

void RemezTest::UpdateGraphs()
{
     Win::HourGlassCursor hgc(true);
     const int filterLength = tbxLength.IntValue;
     vector<Math::Remez::Band> bands;
     Math::Remez::Band band;
     valarray<double> h;
     valarray<double> freq;
     valarray<double> errorData;

     //_________________________________________________________ Low Pass
     band.w1 = 0.0;
     band.w2 = 2.1; //M_PI/100.0;
     band.priory = 1.0;
     band.gain = 1.0;
     bands.push_back(band);
     //
     band.w1 = 2.6;//2.0*M_PI/100.0;
     band.w2 = M_PI;
     band.priory = 1.0;
     band.gain = 0.0;
     bands.push_back(band);
     ////

     //double error = Math::Remez::ImpulseResponse(bands, filterLength, REMEZ_BANDPASS, h, errorData, freq);//This line is for debug
     double error = Math::Remez::ComputeImpulseResponse(bands, filterLength, h); //Low Pass

     //_________________________________________________________ Hilbert
     //band.w1 = 0.1*M_PI;
     //band.w2 = M_PI;
     //band.priory = 1.0;
     //band.gain = 1.0;
     //bands.push_back(band);
     //double error = Math::Remez::ImpulseResponse(bands, filterLength, REMEZ_HILBERT, h, errorData, freq);//This line is for debug
     //double error = Math::Remez::ComputeHilbertImpulseResponse(filterLength, h);

     //________________________________________________________ xyImpulseResponse
     const int len = h.size();
     int i;
     xyImpulseResponse.Graphs[0].SetPointCount(len);
     for(i=0; i<len; i++)
     {
          xyImpulseResponse.Graphs[0][i].x = i;
          xyImpulseResponse.Graphs[0][i].y = h[i];
     }
     xyImpulseResponse.MaxX= len;
     xyImpulseResponse.RefreshAll();

     //________________________________________________________ xyFreqResp

     //xyFreqResp.Graphs[0].SetPointCount(len);
     //for(i=0; i<len; i++)
     //{
     //     xyFreqResp.Graphs[0][i].x = i;//freq[i];
     //     xyFreqResp.Graphs[0][i].y = h[i];
     //}
     //
     valarray<complex<double> > H;
     Math::Dsp::FourierTransformRe(h, H);
     xyFreqResp.Graphs[0].SetPointCount(H.size());
     for(i=0; i<(int)h.size(); i++)
     {
          xyFreqResp.Graphs[0][i].x = i;
          xyFreqResp.Graphs[0][i].y = abs(H[i]);
     }
     xyFreqResp.MaxX = h.size();
     xyFreqResp.RefreshAll();
     //________________________________________________________ xyError
     //const int errorLen = errorData.size();
     //xyError.Graphs[0].SetPointCount(errorLen);
     //for(i=0; i<errorLen; i++)
     //{
     //     xyError.Graphs[0][i].x = freq[i];
     //     xyError.Graphs[0][i].y = errorData[i];
     //}
     //xyError.Graphs[0].Type = Win::Graph::cross;
     //xyError.DivisionCountY = 5;
     //xyError.RefreshAll();
     //
     //wchar_t text[256];
     //_snwprintf_s(text, 256, _TRUNCATE, L"Error = %g", error);
     //this->Text = text;
}

void RemezTest::sldLength_Hscroll(Win::Event& e)
{
     tbxLength.IntValue = sldLength.Position;
     UpdateGraphs();
}

RemezTest

Kaiser Window

The Kaiser Window can be used to design digital Filters. It has two parameters, the length of the filter and beta.
La ventana de Kaiser puede ser usada para diseñar Filtros digitales. Este tiene dos parámetros, la longitud del filtro y la beta.

Problem 2
Repeat the previous problem using the Kaiser Window method, your project must be called KaiserTest.
Repita el problema anterior usando el método de la ventana de Kaiser, su proyecto debe ser llamado KaiserTest.

Solution A
Create a Wintempla Dialog Application. Using Wintempla create the GUI shown below. After inserting the slider for the length of the filter, double click the control and mark the HSCROLL event. Insert another slider for beta.
Cree una aplicación de diálogo de Wintempla. Usando Wintempla cree la GUI mostrada debajo. Después de insertar el slider para longitud del filtro, haga clic doble en el control y marque el evento de HSCROLL. Inserte otro slider para beta.

KaiserTestGui

Solution B
Edit the KaiserTest.h file as shown.
Edite el archivo KaiserTest.h como se muestra.

KaiserTest.h
#pragma once //______________________________________ KaiserTest.h
#include "resource.h"

class KaiserTest: public Win::Dialog
{
public:
     KaiserTest()
     {
     }
     ~KaiserTest()
     {
     }
     void UpdateGraphs();
protected:
     ...
};

Solution C
Edit the KaiserTest.cpp file as shown.
Edite el archivo KaiserTest.cpp como se muestra.

KaiserTest.Cpp
...

void KaiserTest::Window_Open(Win::Event& e)
{
     //________________________________________________________ sldLength
     sldLength.SetRange(2, 200);
     sldLength.Position = 10;
     tbxLength.IntValue = 10;
     //________________________________________________________ sldBeta
     sldBeta.SetRange(1, 3000);
     sldBeta.Position = 100;
     tbxBeta.DoubleValue = 1.0;
     //________________________________________________________ xyFreqResp
     xyFreqResp.CaptionX = L"Frequency";
     xyFreqResp.CaptionY = L"Amplitude";
     xyFreqResp.MinX= 0.0;
     xyFreqResp.MaxX= M_PI;
     xyFreqResp.MinY= 0.0;
     xyFreqResp.MaxY= 1.2;
     xyFreqResp.DivisionCountY = 6;
     xyFreqResp.Graphs.Add();
     //________________________________________________________ xyImpulseResponse
     xyImpulseResponse.CaptionX = L"n";
     xyImpulseResponse.CaptionY = L"h[n]";
     xyImpulseResponse.MinX= -1.0;
     xyImpulseResponse.MaxX= 6.28;
     xyImpulseResponse.MinY= -0.2;
     xyImpulseResponse.MaxY= 0.8;
     xyImpulseResponse.DivisionCountY = 5;
     xyImpulseResponse.Graphs.Add();
     xyImpulseResponse.Graphs[0].Type = Win::Graph::impulse;
     //
     UpdateGraphs();
}

void KaiserTest::sldLength_Hscroll(Win::Event& e)
{
     tbxLength.IntValue = sldLength.Position;
     UpdateGraphs();
}

void KaiserTest::sldBeta_Hscroll(Win::Event& e)
{
     tbxBeta.DoubleValue = sldBeta.Position/100.0;
     UpdateGraphs();
}

void KaiserTest::UpdateGraphs()
{
     Win::HourGlassCursor hgc(true);
     const int filterLength = tbxLength.IntValue;
     const double beta = tbxBeta.DoubleValue;
     //
     valarray<double> h;

     Math::Dsp::ImpulRespLowPass(beta, filterLength, 2.1, h);

     //________________________________________________________ xyImpulseResponse
     const int len = h.size();
     int i;
     xyImpulseResponse.Graphs[0].SetPointCount(len);
     for(i=0; i<len; i++)
     {
          xyImpulseResponse.Graphs[0][i].x = i;
          xyImpulseResponse.Graphs[0][i].y = h[i];
     }
     xyImpulseResponse.MaxX= len;
     xyImpulseResponse.RefreshAll();

     //________________________________________________________ xyFreqResp
     valarray<complex<double> > H;
     Math::Dsp::FourierTransformRe(h, H);
     xyFreqResp.Graphs[0].SetPointCount(H.size());
     for(i=0; i<(int)h.size(); i++)
     {
          xyFreqResp.Graphs[0][i].x = i;
          xyFreqResp.Graphs[0][i].y = abs(H[i]);
     }
     xyFreqResp.MaxX = h.size();
     xyFreqResp.RefreshAll();
}

KaiserTest

Problem 3
Create a Wintempla dialog application called LowPass to filter (in real time) a music wave file. After creating the project open the stdafx.h file and remove the comments from the line #define WIN_DAC_ADC_SUPPORT.
Cree una aplicación de diálogo de Wintempla llamada LowPass para filtrar (en tiempo real) un archivo de música wave. Después de crear el proyecto abra el archivo stdafx.h y remueva los comentarios de la línea #define WIN_DAC_ADC_SUPPORT.

Solution A
Open Wintempla to edit the GUI. Using theShow All Controls in ToolboxShow All Controls in Toolbox from the toolbar insert a DAC control as shown below (on the events tabs, be sure all events are unselected). Insert two labels, two textboxes, two slides (with the Hscroll event), a button, a XyChart and drop down list.
Abra Wintempla y edite la GUI. Usando theShow All Controls in ToolboxShow All Controls in Toolbox desde la barra de herramientas inserta un control DAC como se muestra debajo (en la pestaña de eventos, asegúrate de que todos los eventos estén deseleccionado). Inserta dos etiquetas, dos cajas de texto, dos sliders (con el evento HScroll), un botón, una XyChart y una lista desplegable.

LowPassGui

Solution B
Edit the LowPass.h file and the LowPass.cpp file to implement the three functions of the Mm::IAudioOut interface (Observe the the LowPass class is derived from Mm::IAudioOut). Remember that an interface is used to pass a set of functions to another function or another object.
Edite los archivos LowPass.h y LowPass.cpp para implementar las tres funciones de la interface Mm::IAudioOut (Observa que la clase LowPass se deriva de Mm::IAudioOut). Recuerde que una interface es usada para pasar un conjunto de funciones a otra función u objeto.

LowPass.h
#pragma once //______________________________________ LowPass.h
#include "resource.h"
class LowPass: public Win::Dialog, public Mm::IDataTransfer
{
public:
     LowPass()
     {
     }
     ~LowPass()
     {
     }
     Math::FIRFilter16 fir;
     Mm::WaveFile waveFile;
     void UpdateFilter();
     //______________________________________________________________ Mm::IDataTransfer
     void OnDataStarted(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution);
     void OnData(WAVEHDR* waveHdr);
     void OnDataStopped();
protected:
     ...
};


LowPass.cpp
...
void LowPass::Window_Open(Win::Event& e)
{
     //________________________________________________________ sldFc
     sldFc.SetRange(1, 20000);
     sldFc.Position = 1000;
     tbxFc.IntValue = 1000;
     //________________________________________________________ sldBeta
     sldBeta.SetRange(1, 3000);
     sldBeta.Position = 200;
     tbxBeta.DoubleValue = 2.0;
     //________________________________________________________ xyFR
     xyFR.CaptionX = L"Frequency (Hz)";
     xyFR.CaptionY = L"H[f]";
     xyFR.MinX= 0.0;
     xyFR.MaxX= 22050;
     xyFR.MinY= 0.0;
     xyFR.MaxY= 1.2;
     xyFR.DivisionCountY = 6;
     xyFR.Graphs.Add();
     xyFR.RefreshAll();
     //
     UpdateFilter();
     //
     this->btPlay.Enabled = true;
     this->btStop.Enabled = false;
}

void LowPass::sldFc_Hscroll(Win::Event& e)
{
     tbxFc.IntValue = sldFc.Position;
     UpdateFilter();
}

void LowPass::sldBeta_Hscroll(Win::Event& e)
{
     tbxBeta.DoubleValue = sldBeta.Position/100.0;
     UpdateFilter();
}

void LowPass::UpdateFilter()
{
     const int length = 200;
     const double wc = M_PI*tbxFc.IntValue/22050.0;
     const double beta = tbxBeta.DoubleValue;
     //
     valarray<double> impulseResponse;
     Math::Dsp::ImpulRespLowPass(beta, length, wc, impulseResponse);
     fir.Create(impulseResponse);
     //
     valarray<complex <double> > H;
     Math::Dsp::FourierTransformRe(impulseResponse, H);
     this->xyFR.Graphs[0].SetPointCount(length/2);
     for(int i = 0; i < length/2; i++)
     {
          this->xyFR.Graphs[0][i].x = 2.0*i*22050.0/length;
          this->xyFR.Graphs[0][i].y = abs(H[i]);
     }
     xyFR.RefreshAll();
}

void LowPass::btStop_Click(Win::Event& e)
{
     dacOutput.Stop();
}

void LowPass::btPlay_Click(Win::Event& e)
{
     //________________________________________________________ Open the Wave File
     const wchar_t* error = waveFile.OpenForReading(L"C:\\AudioLab\\music\\Cecilia\\El caso es andar.wav");
     if (error != NULL)
     {
          this->MessageBox(error, L"FilePlayer", MB_OK | MB_ICONERROR);
          return;
     }
     dacOutput.Start(44100, 2, 16, 16384, this);
}

void LowPass::OnDataStarted(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution)
{
     btPlay.Enabled = false;
     btStop.Enabled = true;
     EnableCloseButton(false);
}

void LowPass::OnData(WAVEHDR* waveHdr)
{
     waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength);

     Sys::Sample16 *samples = (Sys::Sample16 *)waveHdr->lpData;
     const int numSamples = waveHdr->dwBytesRecorded/4;
     for (int i = 0; i < numSamples; i++)
     {
          samples[i].channel_1 = fir.GenerateOutputSymmetric(samples[i].channel_1);
          samples[i].channel_2 = samples[i].channel_1;
     }
}

void LowPass::OnDataStopped()
{
     btPlay.Enabled = true;
     btStop.Enabled = false;
     waveFile.Close();
     EnableCloseButton(true);
}


Solution C
Run the program and click the Play button. Select a wave file with two channels, 44100 Hz and 16 bits.
Ejecute el programa y haga clic en el botón de Play. Seleccione un archivo Wave con dos canales, 44100 Hz y 16 bits.

LowPassRun

© Copyright 2000-2019 Wintempla selo. All Rights Reserved. Sep 05 2019. Home