{$EEPROM} StructConst EELEER : Byte = $FF; // erste byte im EEPROM ist nicht zu gebrauchen SampleRate : Float = 48000.0; bessel1 : Array[0..1] Of Float = (1.0, 0.0); bessel2 : Array[0..1] Of Float = (1.361654, 0.618034); bessel3 : Array[0..3] Of Float = (0.999629, 0.477191, 0.756043, 0.0); bessel4 : Array[0..3] Of Float = (0.774254, 0.388991, 1.339664, 0.488904); // usw. Type // Classes of filter TFILTER_FUNCTION = (NO_FILTERING, LPF, HPF, EQ, EQ_LoShelv, EQ_HiShelv, EQ_Notch, EQ_Allpass); // Types of filter functions TFILTER_TYPE = (BUTTERWORTH, LINKWITZ_RILEY, BESSEL, FLAT_FILTER, EQUALIZER); // Types of elementary biquad section transfer functions TBIQUAD_TYPE = (FLAT, LOWPASS_1, LOWPASS_2, HIGHPASS_1, HIGHPASS_2, BANDPASS, PARAMETRIC_EQ, A1A2_LOWPASS, A1A2_HIGHPASS, LOSHELV, HiSHELV, Notch, Allpass); //----------------------------------------------------------- TBiQuad = record TQuadType : TBIQUAD_TYPE; Fb0, Fb1, Fb2, Fa1, Fa2: Float; //5.23 Format Hex_A1,Hex_A2 must -X fpr SigmaDSP //Hex_b0, Hex_b1, Hex_b2, Hex_a1, Hex_a2: Array[0..3] Of Byte; end; // Tbq = Pointer to TBiQuad; TFilter = record // 2 Quad = max 24dB pBiQuad : Array[0..0] Of TBiQuad; // array of pointers to TBiQuad TFilterType : TFILTER_TYPE; TFilterFunction: TFILTER_FUNCTION; Order : Byte; // order CutOff : Word; Güte : Float; Gain : Float; NumOfSections : Byte; // number of biquad sections in filter Enabled : Boolean; AddrDsp : Word; end; TChannel = record Filter : Array[1..14] Of TFilter; // Max 13 BiQuads per Channel // BiQuads 1,2 =HP 24dB; 3,4=TP 24dB ; 5=HighBoost 6=LowBooost 7=Allpass 8-14=EQ1-EQ6 num_of_Filter : Byte; // number of Filters sections in channel Delay : Word; // Max 2000 for all Channels Invert : Byte; Volume : Byte; end; Var Ch1 : TChannel; // 859 Byte //--------------------------------------------------------------------------------------------------------------------- Procedure DesignBiquad(Var TQuad : TBiQuad; Quad_Type : TBIQUAD_TYPE; _fCutOff : Word; _Güte : Float; _gain_db : Float); BEGIN //.. Fbt := bt; // fSampleRate := _fs; // Ffc := _fc; // FQ := _Q; // fGain_db := _gain_db; // Use for A1A2 design only // _A1 := _Q; // _A2 := _gain_db; omega := tan(Float(_fCutOff) * PI / SampleRate); Omega2:= srd(omega); Gain := pow(10.0, _gain_db / 20.0); Case Quad_Type Of //switch(bt) FLAT : TQuad.Fb0 := Gain; TQuad.Fb1 := 0.0; TQuad.Fb2 := 0.0; TQuad.Fa1 := 0.0; TQuad.Fa2 := 0.0; FQ := 0.0; | // 1 LOWPASS_1 : // bei 12MHz dauer 468Hz = 2.14ms norma := 1.0 / (1.0 + omega); TQuad.Fb0 := Gain * omega * norma; TQuad.Fb1 := TQuad.Fb0; TQuad.Fb2 := 0.0; TQuad.Fa1 := (omega - 1.0) * norma; TQuad.Fa2 := 0.0; FQ := 0.5; | // S HIGHPASS_1 : // H(s) = ------- // 1 + S norma := 1.0 / (1.0 + omega); TQuad.Fb0 := Gain * norma; TQuad.Fb1 := - TQuad.Fb0; TQuad.Fb2 := 0.0; TQuad.Fa1 := (omega - 1.0) * norma; TQuad.Fa2 := 0.0; FQ := 0.5; | LOWPASS_2 : // bei 12MHz dauer 368Hz = 2.71ms norma := 1.0 / (Omega2 + omega / _Güte + 1.0); TQuad.Fb0 := Omega2 * norma * Gain; TQuad.Fb1 := 2.0 * TQuad.Fb0; TQuad.Fb2 := TQuad.Fb0; TQuad.Fa1 := 2.0 * norma * (Omega2 - 1.0); TQuad.Fa2 := norma * (Omega2 - omega / _Güte + 1.0); | // s^2 HIGHPASS_2 : // H(s) = --------------- // s^2 + s/Q + 1 norma := 1.0 / (Omega2 + omega / _Güte + 1.0); TQuad.Fb0 := norma * Gain; TQuad.Fb1 := - 2.0 * TQuad.Fb0; TQuad.Fb2 := TQuad.Fb0; TQuad.Fa1 := 2.0 * norma * (Omega2 - 1.0); TQuad.Fa2 := norma * (Omega2 - omega / _Güte + 1.0); | // s/Q A1A2_LOWPASS : norma := 1.0 / (Omega2 + _A1 * omega + _A2); TQuad.Fb0 := Omega2 * norma; TQuad.Fb1 := 2.0 * TQuad.Fb0; TQuad.Fb2 := TQuad.Fb0; TQuad.Fa1 := 2.0 * (Omega2 - _A2) * norma; TQuad.Fa2 := (Omega2 - _A1 * omega + _A2) * norma; | A1A2_HIGHPASS : norma := 1.0 / (1.0 + _A1 * omega + _A2 * Omega2); TQuad.Fb0 := norma; TQuad.Fb1 := - 2.0 * TQuad.Fb0; TQuad.Fb2 := TQuad.Fb0; TQuad.Fa1 := 2.0 * (_A2 * Omega2 - 1.0) * norma; TQuad.Fa2 := (1.0 - _A1 * omega + _A2 * Omega2) * norma; | endcase; end; //----------------------------------------------------------------------------- Procedure FilterDesignFilter(Var tFilt : TFilter; _FILTER_TYPE : Tfilter_type; _FILTER_FUNCTION : Tfilter_function; _fFc : Word; _order : Byte; _gain_db : Float; _Qg : Float); Var ci : Byte; fi_step : Float; A1, A2 : Float; bt : TBIQUAD_TYPE; Q, fi : Float; s, _q, qtmp : TCplx; besselp : Array[0..4] Of Float; I : Word; te : Byte; ftemp : Float; BEGIN //..self.DeallocateBiquad; // Clean garbage first IF _order < 1 THEN _order := 1; endif; tFilt.Order := _order; ftemp:= Float(_order) / 2; I:= Word(ceil(ftemp)); tFIlt.NumOfSections := Byte(i); // Anzahl Quads tFilt.TFilterType := _filter_type; tFilt.TFilterFunction := _filter_function; tFilt.Güte := _Qg; tFilt.Gain := _gain_db; // Design filter Case _filter_type Of FLAT_FILTER : //.. self.AllocateBiquad; tFilt.CutOff := Word(SampleRate / 2 ); tFilt.TFilterFunction := NO_FILTERING; For ci := 0 to tFilt.NumOfSections Do //pBq[ci] := TBiquad.Create; DesignBiquad(tFilt.pBiQuad[ci], FLAT, _Ffc, 0.0, 0.0); endfor; | // Note: Bessel is designed using BLT. // The fase is linear if Ffc is much less then fs/2 // Table supports Bessel up to 12-th order BESSEL : IF (_order > 12) THEN _order := 12; endif; tFilt.Order := _order; ftemp:= ceil(Float(tFilt.Order) / 2.0); I:= Word(ftemp); tFIlt.NumOfSections := Byte(i); //..self.AllocateBiquad; Case tFilt.tFilterFunction Of LPF : bt := A1A2_LOWPASS; | HPF : bt := A1A2_HIGHPASS; | endcase; // case filter_function // 6, 12,18 und 24 dB Case Integer(_order) Of // blöde lösung 1 : For I := 0 to 1 Do //High(bessel1) Do besselp[I] := bessel1[I]; endfor; | 2 : For I := 0 to 1 Do // High(bessel2) Do besselp[I] := bessel2[I]; endfor; | 3 : For I := 0 to 3 Do //High(bessel3) Do besselp[I] := bessel3[I]; endfor; | 4 : For I := 0 to 3 Do //High(bessel4) Do besselp[I] := bessel4[I]; endfor; | endcase; // order I := 0; For ci := 0 to tFilt.NumOfSections - 1 Do A1 := besselp[I]; A2 := besselp[succ(I)]; Inc(I, 2); DesignBiquad(tFilt.pBiQuad[ci], bt, _Ffc, A1, A2); endfor; | // Bessel EQUALIZER : //self.DeallocateBiquad; // Clean garbage first tFilt.CutOff := _Ffc; Q := _Qg; tFilt.Order := 2; tFilt.NumOfSections := 1; tFilt.TFilterType := _filter_type; tFilt.TFilterFunction := _filter_function; //.. self.AllocateBiquad; Case _FILTER_FUNCTION Of EQ_LoShelv : DesignBiquad(tFilt.pBiQuad[0], LOSHELV, _Ffc, _Qg, _gain_db); | EQ_HiShelv : DesignBiquad(tFilt.pBiQuad[0], HiSHELV, _Ffc, _Qg, _gain_db); | EQ_AllPass : _gain_db := 0; DesignBiquad(tFilt.pBiQuad[0], Allpass, _Ffc, _Qg, _gain_db); | EQ_Notch : DesignBiquad(tFilt.pBiQuad[0], Notch, _Ffc, _Qg, _gain_db); | EQ : IF ((Q > 0.0) And (_gain_db <> 0.0)) THEN DesignBiquad(tFilt.pBiQuad[0], PARAMETRIC_EQ, _Ffc, _Qg, _gain_db); ELSE DesignBiquad(tFilt.pBiQuad[0], FLAT, _Ffc, _Qg, _gain_db); endif; | endcase; // EQUALIZER _Filter_Function | endcase; // filter_type // SetEnable(True); // case _filter_type of IF Integer(tFilt.TFilterType) < 3 THEN // Butter,Link,Bessel //..fFiltTypCombo.ItemIndex := Integer(FFilterType); //..fOrderEdit.Value := FOrder * 6; endif; end; //------------------------------------------------------------- // test // FilterDesignFilter(Ch1.Filter[1], BESSEL, LPF, 250, 2, 6.0, 0.70);