プログラミングず音楜バタヌワヌス呚波数フィルタヌ。 パヌト3

みなさんこんにちは CでのVSTシンセサむザヌの䜜成に関する蚘事の第3郚を読んでいたす。 前のパヌトでは、 VSTプラグむンを䜜成するためのSDKずラむブラリを怜蚎し、信号振幅を制埡するためのオシレヌタヌずADSR゚ンベロヌプのプログラミングを怜蚎したした。


このパヌトでは、シンセサむザヌができない呚波数フィルタヌの蚈算ずコヌディングの方法を説明したす。 たた、むコラむザヌがなければ、サりンド凊理は考えられたせん。


゜ヌスコヌドず、 NAudioラむブラリ.NETのサりンドを操䜜するためのラむブラリのむコラむザヌの䜿甚に぀いお怜蚎したす。


泚意-倚くのマタンがありたす -フィルタヌ係数の匏を蚈算したす。


私のシンセサむザヌの゜ヌスコヌドはGitHubで入手できたす 。



VSTむコラむザヌプラグむンFab Filter Pro Qのスクリヌンショット



蚘事のサむクル


  1. CWPFでVSTiシンセサむザヌを理解しお蚘述したす
  2. ADSR信号゚ンベロヌプ
  3. バタワヌス呚波数フィルタヌ
  4. 遅延、歪み、およびパラメヌタヌ倉調

目次


  1. むコラむザヌ
  2. フヌリ゚倉換フィルタリング
  3. デゞタルフィルタヌ
  4. バタヌワヌスフィルタヌを䜿甚する理由
  5. LFフィルタヌ匏出力
  6. ハむパスフィルタヌずバンドパスフィルタヌの匏の導出
  7. 埗られた匏に埓ったバタワヌスフィルタヌプログラミング
  8. NAudio Libraryのバンドむコラむザヌ
  9. フィルタヌを蚈算するためのプログラム
  10. 参照資料


むコラむザヌ


倚くの堎合、サりンドを凊理するずきは、そのキャラクタヌ/色/音色を倉曎したす。 音をより䜎音にしたり、高呚波数を削陀したり、その逆を行ったりしお、音を「透明」にしお、䞭倮ず䞊郚のみを残したす。 サりンド凊理に携わっおいない人の倚くは、 むコラむザヌが䜕であるかを知っおいるず確信しおいたす。スピヌカヌ、ミュヌゞックセンタヌ、テヌプレコヌダヌ、プレヌダヌなどがありたす。 むコラむザヌはフィルタヌのセットで、それぞれが遞択した呚波数垯域の信号の振幅を倉曎したす。 家庭甚スピヌカヌでは、これは通垞2〜3個のノブです。䜎呚波数、䞭音、䞊音、固定呚波数垯域です。


Winampむコラむザヌには10個の定矩枈みバンドが既にありたす。



Winamp Equalizerスクリヌンショット


サりンド凊理の䞖界では、あらゆる味ず色に察応する倚くのむコラむザヌプラグむンがありたす。 Fab Filter Pro Qプラグむン蚘事の冒頭のスクリヌンショットは、倚数のバンドを䜜成しおパラメヌタヌを線集できるグラフィックむコラむザヌです。


むコラむザヌの各垯域は、実際には呚波数フィルタヌです。 呚波数フィルタヌは、信号の音色/呚波数応答を倉曎したす。 ゚レクトロニクスでは、倚くの皮類ずフィルタヌの分類があり、察応する特性ずパラメヌタヌがありたす- りィキペディアを芋おください 。
最も単玔なフィルタヌであるロヌパス、ハむパス、バンドパスフィルタヌを怜蚎し、プログラムしたす。



フヌリ゚倉換フィルタリング


理論的には、信号を䜿甚しお離散フヌリ゚倉換を行い、呚波数を凊理しおから逆倉換を行うこずを気にする人はいたせん。


DFTの実装に぀いお考えない堎合は、このアプロヌチを非垞に盎感的で簡単にプログラミングできるず思いたすここでも、DFTを䜕らかの皮類のものから自分でコヌディングしない堎合。


アプロヌチの短所-最初に、DFTは、サむズが2のべき乗であるサンプルの配列から入力を受け取りたす。 これは、出力信号がすでに遅延しおいるこずを意味したす。 次に、512番目のサンプルごずに、DFT、信号呚波数凊理、逆DFTずいうアルゎリズムを䜜成したす。 これらは小さな蚈算ではありたせん。 第䞉に、デゞタル信号凊理の支持者が知っおいる、ただ短所ず埮劙さがありたす。


DFTの䜿甚は考慮したせんが、デゞタルフィルタヌの理論を芋おみたしょう。 サンプル倀を凊理し、入力サンプル配列の長さに応じお線圢蚈算の耇雑さを持぀フィルタヌを䜜成したしょう。



デゞタルフィルタヌ


私は本のデゞタル信号凊理実甚的なアプロヌチからほずんどの情報ず匏の掟生物を取りたした-私はそれを匷くお勧めしたす、それはロシア語版である- デゞタル信号凊理です。 実甚的なアプロヌチ 、興味のある方はりェブ䞊でPDFを芋぀けるでしょう。


重芁なポむントを述べたい。 フィルタヌの構築ず蚈算のトピックは本圓に非垞に耇雑で、倚くの埮劙さずニュアンスが含たれおおり、理論の知識ず理解が必芁です。 この蚘事では、読者がこれらの匏の由来を理解できるように、バタヌワヌスフィルタヌの匏を蚈算する方法を瀺したす。 しかし、なぜそのような初期の匏、なぜ正確にそのような眮換が必芁なのかは、デゞタル信号凊理の深局理論に突入するこずによっおのみ理解できたす。


フィルタヌコヌドをグヌグルで調べ始めたずき、すぐに理解できない数孊的コヌドをたくさん芋぀けたので、そのような蚈算匏がどこから来たのかを少なくずも少し知りたいず思いたした。 オシレヌタヌ、゚ンベロヌプ、遅延-これらのコンポヌネントの動䜜を個人的に理解しおプログラミングするこずは、私には盎感的ですが、フィルタヌではありたせん。 この蚘事では、デゞタル信号凊理ぞの関心を喚起したいですこのトピックをより完党に理解したいずいう願望があれば嬉しいです。


畳み蟌み 、 フィルタヌのむンパルス応答、フィルタヌの 䌝達関数などの甚語を少なくずも少し知る必芁がありたす 。



理想的なフィルタヌの呚波数応答の近䌌゜ビ゚トの教科曞の写真、゜ヌスが芋぀かりたせんでした


フィルタヌは信号を倉曎し、遞択された呚波数を「陀去」したす。 既存のフィルタヌは完党ではありたせん。 垯域幅-フィルタヌが「圱響しない」呚波数の垯域䞀郚ありたす 倉曎は、䞍完党なフィルタヌの機胜です。 抑制垯域-䞍芁な呚波数の垯域。 遷移垯域では、呚波数の䜎䞋が発生したす。 圓然、フィルタヌは、通過垯域の歪みがどれだけ小さく、抑制垯域の呚波数をどれだけ抑制し、遷移垯域がどれだけ狭いかによっお、「理想的な」フィルタヌに近くなりたす。 フィルタヌにはさたざたな「近䌌」がありたす-チェビシェフ、バタヌボットフィルタヌなど-本やネットワヌクのオヌプンスペヌスにありたす。



バタヌワヌスフィルタヌを䜿甚する理由


すべおが非垞に単玔で、バタワヌスフィルタヌの呚波数応答は通過垯域呚波数で可胜な限り滑らかです-私芋、最も重芁なこずは、通過垯域の信号を損なうこずではありたせん。



異なる次数のバタヌワヌスロヌパスフィルタヌの察数呚波数応答りィキペディアからのスクリヌンショット



LFフィルタヌ匏出力


s平面䞊のバタヌワヌスフィルタヌの䌝達関数は、次の匏で蚘述されたす。


偶数nず
奇数n


ここで、nはフィルタヌの次数です。カットオフ呚波数wでの振幅は-3n dBであり、振幅呚波数応答はオクタヌブごずに-6n dB枛衰したす。


たたみ蟌み係数を取埗するには、次の圢匏でz平面の䌝達関数を取埗する必芁がありたす。



2次フィルタヌの䌝達関数オクタヌブあたり-6 dBの枛衰を芋぀け、Hsn = 2の匏に代入したす。



次に、フィルタヌの畳み蟌みは次のようになりたす。



カットオフ呚波数w信号振幅が-3 dBになるずFcにサンプリング呚波数1秒あたりのサンプル数をヘルツ単䜍で䞎えおみたしょう。


匏では、非正芏化呚波数を䜿甚する必芁がありたす。 眮き換えを行いたすバンドパスフィルタヌには、通過垯域を決定する2぀の呚波数w1ずw2がありたす。



ロヌパスフィルタヌを蚈算する堎合は、倉換を行う必芁がありたす。䌝達関数のパラメヌタヌsを眮き換えたす。



他のタむプのフィルタヌハむパス、バンドパス、ノッチを蚈算するには、他の眮換を行う必芁がありたす。 それらは、 デゞタル信号凊理の本で議論されおいたす。 パヌト8.8.2以降の蚘事の実甚的なアプロヌチ 。


次に、zプレヌンに移動するには、倉曎を行いたす。



分析蚈算には、Mathematicaパッケヌゞを䜿甚したした。



zの倚項匏の圢で分子ず分母を取埗する必芁がありたす。 分母Hzの項を共通分母に枛らしたす。 これを行うには、分母項の最倧公玄数GCDを芋぀け、元の関数Hzの分子ず分母をそれに分割したす。



CoefficientList関数を䜿甚しお、結果の倚項匏のべき乗で係数を芋぀けたす。



すべおが正盎に行われた堎合、条件により、a0は1に等しくなりたす-すべおの係数をa0で陀算したすコヌディングでは、陀算せずに前の匏を䜿甚したす。




ハむパスフィルタヌずバンドパスフィルタヌの匏の導出


ハむパスフィルタヌの匏の導出は、倉換が異なるロヌパスフィルタヌに䌌おいたす。




バンドパスフィルタヌの匏を導出するために、倉換が適甚されたす。



眮換を行うず、Hzの分子ず分母の倚項匏の次数が2倍になるため眮換にはs ^ 2がありたす、フィルタヌの次数は2倍になりたす。 したがっお、最初はn = 1に察しお関数Hsを䜿甚したす。




埗られた匏に埓ったバタワヌスフィルタヌプログラミング


フィルタヌには、フィルタヌタむプロヌパス、ハむパス、バンドパスずカットオフ呚波数wの2぀のパラメヌタヌがありたす。 バンドパスフィルタヌでは、カットオフ呚波数を通過垯域の䞭倮の呚波数ず芋なしたす。 通過垯域は、呚波数間隔[w-w / 4、w + w / 4]ずしお定矩されたすここで、より耇雑で論理的な察数の法則を自由に遞択できたす。


蚈算された匏を䜿甚しお、係数b0、b1、b2、a1、a2条件によっおa0は1に等しいを決定したずしたす。 フィルタヌ操䜜アルゎリズムは畳み蟌みに瞮小され、サンプルごずに順番に実行されたす。



ynは、蚈算される新しいサンプル倀です。 xnはサンプルの珟圚の倀、yn-1ずyn-2は前の2぀の蚈算されたサンプル、xn-1ずxn-2はサンプルの前の入力倀です。


以前のサンプルの蚘憶を敎理する必芁がありたす。 埪環バッファヌでは賢くなく、単玔で明確にする3぀の芁玠の2぀の配列。 新しい倀をこの配列に「プッシュ」するたびに、サンプルの叀い倀を順番にコピヌしたす。


単玔なクラスを取埗したす。


class BiquadConvolutionTable { public double B0, B1, B2, A1, A2; private readonly double[] _x = new double[3]; private readonly double[] _y = new double[3]; public double Process(double s) { // ""   _x[2] = _x[1]; _x[1] = _x[0]; _x[0] = s; _y[2] = _y[1]; _y[1] = _y[0]; //  _y[0] = B0 * _x[0] + B1 * _x[1] + B2 * _x[2] - A1 * _y[1] - A2 * _y[2]; return _y[0]; } } 

フィルタヌのワむダヌフレヌムクラスを蚘述したしょう 最初の蚘事のシンセサむザヌアヌキテクチャを参照 。 BiquadConvolutionTableクラスは、単䞀の信号で動䜜したす。 1぀のチャネルで-モノラル。 したがっお、巊右のチャネル甚に2぀のBiquadConvolutionTableが必芁です。


フィルタヌを正しく適甚するには、着信シヌケンスのすべおのサンプルにBiquadConvolutionTable.Process関数を連続しお適甚し、結果のサンプル配列に入力する必芁がありたす。


BiquadConvolutionTableの係数の蚈算は、関数CalculateCoefficientsによっお実行されたす。


 public enum EFilterPass { None, LowPass, HiPass, BandPass } public class ButterworthFilter : SyntageAudioProcessorComponentWithParameters<AudioProcessor>, IProcessor { private readonly BiquadConvolutionTable _tablel; private readonly BiquadConvolutionTable _tabler; public EnumParameter<EFilterPass> FilterType { get; private set; } public FrequencyParameter CutoffFrequency { get; private set; } public ButterworthFilter(AudioProcessor audioProcessor) : base(audioProcessor) { _tablel = new BiquadConvolutionTable(); _tabler = new BiquadConvolutionTable(); } public override IEnumerable<Parameter> CreateParameters(string parameterPrefix) { FilterType = new EnumParameter<EFilterPass>(parameterPrefix + "Pass", "Filter Type", "Filter", false); CutoffFrequency = new FrequencyParameter(parameterPrefix + "Cutoff", "Filter Cutoff Frequency", "Cutoff"); return new List<Parameter> {FilterType, CutoffFrequency}; } public void Process(IAudioStream stream) { if (FilterType.Value == EFilterPass.None) return; var count = Processor.CurrentStreamLenght; var lc = stream.Channels[0]; var rc = stream.Channels[1]; for (int i = 0; i < count; ++i) { var cutoff = CutoffFrequency.Value; CalculateCoefficients(cutoff); var ls = _tablel.Process(lc.Samples[i]); lc.Samples[i] = ls; var rs = _tabler.Process(rc.Samples[i]); rc.Samples[i] = rs; } } private void CalculateCoefficients(double cutoff) { ... } } 

関数CalculateCoefficientsはルヌプ内で毎回呌び出されたす-なぜですか 次の蚘事では、パラメヌタヌの倉調時間の倉化に぀いお説明したす。したがっお、カットオフ呚波数が倉化する可胜性がありたす。぀たり、係数を再蚈算する必芁がありたす。 もちろん、急いで、カットオフ呚波数の倉化にサブスクラむブし、プロセッサに既にある係数を蚈算する必芁がありたす。 しかし、これらの蚘事では最適化を扱いたせん。目暙はフィルタヌをコヌディングするこずです。


係数の蚈算匏に埓っお関数CalculateCoefficientsをコヌディングしたす。
非正芏化された呚波数を䜿甚する必芁があるこずを思い出しおください。 亀換



係数b0、b1、b2、a0、a1、a2のすべおの匏を曞き留めたす。 蚈算埌、a0が1になるようにすべおの係数をa0で陀算する必芁がありたす。


 private double TransformFrequency(double w) { return Math.Tan(Math.PI * w / Processor.SampleRate); } private void CalculateCoefficients(double cutoff) { double b0, b1, b2, a0, a1, a2; switch (FilterType.Value) { case EFilterPass.LowPass: { var w = TransformFrequency(cutoff); a0 = 1 + Math.Sqrt(2) * w + w * w; a1 = -2 + 2 * w * w; a2 = 1 - Math.Sqrt(2) * w + w * w; b0 = w * w; b1 = 2 * w * w; b2 = w * w; } break; case EFilterPass.HiPass: { var w = TransformFrequency(cutoff); a0 = 1 + Math.Sqrt(2) * w + w * w; a1 = -2 + 2 * w * w; a2 = 1 - Math.Sqrt(2) * w + w * w; b0 = 1; b1 = -2; b2 = 1; } break; case EFilterPass.BandPass: { var w = cutoff; var d = w / 4; //     [w * 3 / 4, w * 5 / 4] var w1 = Math.Max(w - d, CutoffFrequency.Min); var w2 = Math.Min(w + d, CutoffFrequency.Max); w1 = TransformFrequency(w1); w2 = TransformFrequency(w2); var w0Sqr = w2 * w1; // w0^2 var wd = w2 - w1; // W a0 = -1 - wd - w0Sqr; a1 = 2 - 2 * w0Sqr; a2 = -1 + wd - w0Sqr; b0 = -wd; b1 = 0; b2 = wd; } break; default: throw new ArgumentOutOfRangeException(); } _tablel.B0 = _tabler.B0 = b0 / a0; _tablel.B1 = _tabler.B1 = b1 / a0; _tablel.B2 = _tabler.B2 = b2 / a0; _tablel.A1 = _tabler.A1 = a1 / a0; _tablel.A2 = _tabler.A2 = a2 / a0; } 

完党なButterworthFilterクラスコヌド
 public enum EFilterPass { None, LowPass, HiPass, BandPass } public class ButterworthFilter : SyntageAudioProcessorComponentWithParameters<AudioProcessor>, IProcessor { private class BiquadConvolutionTable { public double B0, B1, B2, A1, A2; private readonly double[] _x = new double[3]; private readonly double[] _y = new double[3]; public double Process(double s) { // ""   _x[2] = _x[1]; _x[1] = _x[0]; _x[0] = s; _y[2] = _y[1]; _y[1] = _y[0]; //  _y[0] = B0 * _x[0] + B1 * _x[1] + B2 * _x[2] - A1 * _y[1] - A2 * _y[2]; return _y[0]; } } private readonly BiquadConvolutionTable _tablel; private readonly BiquadConvolutionTable _tabler; public EnumParameter<EFilterPass> FilterType { get; private set; } public FrequencyParameter CutoffFrequency { get; private set; } public ButterworthFilter(AudioProcessor audioProcessor) : base(audioProcessor) { _tablel = new BiquadConvolutionTable(); _tabler = new BiquadConvolutionTable(); } public override IEnumerable<Parameter> CreateParameters(string parameterPrefix) { FilterType = new EnumParameter<EFilterPass>(parameterPrefix + "Pass", "Filter Type", "Filter", false); CutoffFrequency = new FrequencyParameter(parameterPrefix + "Cutoff", "Filter Cutoff Frequency", "Cutoff"); return new List<Parameter> {FilterType, CutoffFrequency}; } public void Process(IAudioStream stream) { if (FilterType.Value == EFilterPass.None) return; var count = Processor.CurrentStreamLenght; var lc = stream.Channels[0]; var rc = stream.Channels[1]; for (int i = 0; i < count; ++i) { var cutoff = CutoffFrequency.Value; CalculateCoefficients(cutoff); var ls = _tablel.Process(lc.Samples[i]); lc.Samples[i] = ls; var rs = _tabler.Process(rc.Samples[i]); rc.Samples[i] = rs; } } private double TransformFrequency(double w) { return Math.Tan(Math.PI * w / Processor.SampleRate); } private void CalculateCoefficients(double cutoff) { double b0, b1, b2, a0, a1, a2; switch (FilterType.Value) { case EFilterPass.LowPass: { var w = TransformFrequency(cutoff); a0 = 1 + Math.Sqrt(2) * w + w * w; a1 = -2 + 2 * w * w; a2 = 1 - Math.Sqrt(2) * w + w * w; b0 = w * w; b1 = 2 * w * w; b2 = w * w; } break; case EFilterPass.HiPass: { var w = TransformFrequency(cutoff); a0 = 1 + Math.Sqrt(2) * w + w * w; a1 = -2 + 2 * w * w; a2 = 1 - Math.Sqrt(2) * w + w * w; b0 = 1; b1 = -2; b2 = 1; } break; case EFilterPass.BandPass: { var w = cutoff; var d = w / 4; //     [w * 3 / 4, w * 5 / 4] var w1 = Math.Max(w - d, CutoffFrequency.Min); var w2 = Math.Min(w + d, CutoffFrequency.Max); w1 = TransformFrequency(w1); w2 = TransformFrequency(w2); var w0Sqr = w2 * w1; // w0^2 var wd = w2 - w1; // W a0 = -1 - wd - w0Sqr; a1 = 2 - 2 * w0Sqr; a2 = -1 + wd - w0Sqr; b0 = -wd; b1 = 0; b2 = wd; } break; default: throw new ArgumentOutOfRangeException(); } _tablel.B0 = _tabler.B0 = b0 / a0; _tablel.B1 = _tabler.B1 = b1 / a0; _tablel.B2 = _tabler.B2 = b2 / a0; _tablel.A1 = _tabler.A1 = a1 / a0; _tablel.A2 = _tabler.A2 = a2 / a0; } } 


NAudio Libraryのバンドむコラむザヌ


サりンド、.NETのさたざたな圢匏のサりンドファむルを操䜜するための優れたNAudioラむブラリがありたす。


これには、フィルタリング、 畳み蟌み 、 ゲヌト 、 ゚ンベロヌプ 、 FFTなどの興味深い機胜を備えたNAudio.Dsp名前空間が含たれおいたす。


Equalizerクラス䟋のNAudioWpfDemo.EqualizationDemo名前空間からを考えおください。これにより、信号をむコラむズできたす。 このクラスはISampleProviderを実装したす。ISampleProviderは、読み取り関数float []バッファヌ、intオフセット、intカりントでバッファヌサンプルの配列を凊理倉曎したす。


コンストラクタヌは、むコラむザヌの「バンド」を蚘述するEqualizerBand構造䜓の配列を受け入れたす。


 class EqualizerBand { public float Frequency { get; set; } public float Gain { get; set; } public float Bandwidth { get; set; } } 

ここで、Frequencyは、Qパラメヌタヌ垯域幅、 フィルタヌ品質係数 、ゲむンGain dBを持぀垯域の䞭心呚波数です。


実装を芋るず、EqualizerBandの各バンドは、ピヌキングフィルタヌずしお䜿甚されるBiQuadFilterクラスに察応しおいたす。 すべおのフィルタヌは、連続しお䜿甚される信号を倉曎したす。


EqualizerBandクラスはバタヌワヌスフィルタヌの実装であり、フィルタヌタむプずパラメヌタヌの遞択肢が豊富です。 実装を芋るず、同様の匏ず係数を芋るこずができたす。


Equalizerクラスの䜿甚䟋は、EqualizationDemoViewModelクラスのNAudioWpfDemoプロゞェクトにありたす。



フィルタヌを蚈算するためのプログラム


デゞタルフィルタヌの先駆者はアナログフィルタヌでした。 アナログ回路ずアナログ信号凊理の理論は、埌にデゞタル信号凊理の理論に成長したした。


考慮されるバタワヌスフィルタヌず係数の蚈算匏に぀いおは、アナログ回路を組み立おるこずができたす。


回路、それらの芁玠のパラメヌタヌ、さたざたなフィルタヌの畳み蟌み係数を蚈算および構築するための倚くのプログラムがありたす。
「フィルタヌ蚈算゜フトりェア」でグヌグル怜玢できたす。



アむオワヒルズ゜フトりェアRFフィルタヌデザむナヌ


次の蚘事では、パラメヌタの遅延 、 歪み 、倉調の効果に぀いお説明したす。


すべおに良い プログラミングで頑匵っおください



参照資料


以前の蚘事の蚘事ず本のリストを忘れずに芋おください。


  1. デゞタル信号凊理実甚的なアプロヌチ 。 ロシア語版- デゞタル信号凊理。 実甚的なアプロヌチ 。
  2. Habrの蚘事「フヌリ゚倉換に関する簡単な蚀葉で」
  3. Wikiのバタヌワヌスフィルタヌ
  4. バタヌワヌスフィルタヌ蚈算コヌドを䜿甚したGithubリポゞトリ
  5. 離散信号ずシステム、第7章。FIRおよびIIRフィルタヌ
  6. ロヌパスフィルタヌはプログラムでどのように機胜したすか dsp.stackexchange.com/
  7. デゞタルバタヌワヌスフィルタヌずチェビシェフフィルタヌの蚭蚈
  8. オヌディオEQクックブック
  9. アむオワヒルズ゜フトりェア-デゞタルおよびアナログフィルタヌ


Source: https://habr.com/ru/post/J313062/


All Articles