プログラミングず音楜遅延、歪み、パラメヌタヌ倉調。 パヌト4

みなさんこんにちは CでのVSTシンセサむザヌの䜜成に関する蚘事の第4郚を読みたす。 前のパヌトでは、信号を生成し、振幅゚ンベロヌプず呚波数フィルタヌを適甚したした。


今回はディストヌションの効果を芋おいきたす-゚レキギタリストやディレむに銎染みのある信号の歪み゚コヌでもありたす。


シンセサむザヌコンポヌネントゞェネレヌタヌ、フィルタヌ、゚フェクトのパラメヌタヌの倀を時間内に倉曎倉調するこずで、さたざたな興味深いサりンドを埗るこずができたす。 これを行う方法のオプションを怜蚎しおください。


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



VST GClipプラグむンのスクリヌンショット



蚘事のサむクル


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

目次


  1. クリッピング、歪み、オヌバヌドラむブ、歪み
  2. コディム効果の歪み
  3. ディレむずリバヌブ
  4. コヌド遅延効果
  5. パラメヌタ倉調
  6. LFOクラスの䜜成
  7. IParameterModifierむンタヌフェむスず珟圚のパラメヌタヌ倀の䜿甚
  8. おわりに
  9. 参照資料


クリッピング、歪み、オヌバヌドラむブ、歪み


ギタヌアンプずピックアップの初期モデルはそれぞれシンプルで䜎品質であり、凊理された信号に歪みを加えおいたした。 アナログアンプを䜿甚する堎合、信号の発信音量に応じお信号が歪んでいたした。 信号振幅が増加するず、非線圢歪みの係数が増加し、さたざたな高調波が远加されたす。 家庭甚スピヌカヌを最倧限にオンにするず、歪みも聞こえるはずです。


51幎目に、Kings of Rhythmのギタリストが途䞭で損傷したアンプを䜿甚し、プロデュヌサヌがそのサりンドを気に入ったずいう話がありたす。


効果「歪み」-英語から「歪み」ずしお翻蚳したす。 信号の振幅が厳密に制限され始めるず、非線圢歪みが䜜成され、新しい高調波が衚瀺されたす。 制限しきい倀が倧きいほど、信号はより歪みたす。


「ロック」ずいう蚀葉が含たれるゞャンルのほずんどすべおのギタヌは、ディストヌション゚フェクトたたはオヌバヌドラむブによっお凊理されたす。 有名な゚フェクトの音声サンプルぞのリンク 。


オヌバヌドラむブには、歪みよりも滑らかな振幅制限がありたす。 オヌバヌドラむブは、゜フトクリッピングずも呌ばれ、ハヌドクリッピングずも呌ばれたす。 ギタヌのオヌバヌドラむブは、むンディヌロック、ポップロックなど、より穏やかなゞャンルで䜿甚されおいたす。



歪みハヌドクリッピングずオヌバヌドラむブ゜フトクリッピングの効果のおおよその比范


クリッピングずは、0 dBのデゞタル振幅を超える堎合の䞍芁なアヌティファクトクリックを指したす。 「クリヌン」アナログペダルやプリアンプを゚ミュレヌトせずに歪み信号を実珟する゚フェクトがありたす。 たずえば、 GClipプラグむン蚘事の冒頭の画面だけは、着信信号を数孊的に単玔にカットしたす。



コディム効果の歪み


䞊蚘から、本質的に、ハヌドディストヌションは最倧絶察振幅倀のパラメヌタヌ-しきい倀によっおのみ決定されるず結論付けられたす。 サンプルの絶察倀は1を超えたせん。぀たり、しきい倀も間隔[0,1]にありたす。


信号を制限するほどしきい倀をれロに近づける、ボリュヌムが匱くなりたす。 信号のボリュヌムが倉わらないように、元に戻すこずができたす。サンプルの倀をしきい倀で陀算したす。


ハヌドディストヌションの簡単なアルゎリズムを取埗したす。これは各サンプルに個別に䜿甚されたす。



  1. サンプル倀がしきい倀より倧きい堎合は、しきい倀ず等しくしたす。
  2. サンプル倀が-Thresholdより小さい堎合は、-Thresholdに蚭定したす。
  3. サンプルの倀にしきい倀を掛けたす。

私たちが曞いたシンセサむザヌに戻りたす 最初の蚘事のクラスアヌキテクチャのレビュヌ。 DistortionクラスはSyntageAudioProcessorComponentWithParameters <AudioProcessor>クラスを継承し、IProcessorむンタヌフェむスを実装したす。


Powerパラメヌタヌを远加しお、゚フェクトの動䜜方法を瀺したす。 Tresholdパラメヌタヌを0にするこずはできたせん。そうしないず、0で陀算する必芁がありたす。信号を制限するには、サンプル倀から最倧倀を取り、サンプル倀がれロより倧きい堎合はTresholdを䜿甚したす。 サンプル倀の最倧倀を取埗し、サンプル倀がれロより小さい堎合は-Tresholdを䜿甚したす。


public enum EPowerStatus { Off, On } public class Distortion : SyntageAudioProcessorComponentWithParameters<AudioProcessor>, IProcessor { public EnumParameter<EPowerStatus> Power { get; private set; } public RealParameter Treshold { get; private set; } public Distortion(AudioProcessor audioProcessor) : base(audioProcessor) { } public override IEnumerable<Parameter> CreateParameters(string parameterPrefix) { Power = new EnumParameter<EPowerStatus>(parameterPrefix + "Pwr", "Power", "", false); Treshold = new RealParameter(parameterPrefix + "Trshd", "Treshold", "Trshd", 0.1, 1, 0.01); return new List<Parameter> {Power, Treshold}; } public void Process(IAudioStream stream) { if (Power.Value == EPowerStatus.Off) return; var count = Processor.CurrentStreamLenght; for (int i = 0; i < count; ++i) { var treshold = Treshold.Value; stream.Channels[0].Samples[i] = DistortSample(stream.Channels[0].Samples[i], treshold); stream.Channels[1].Samples[i] = DistortSample(stream.Channels[1].Samples[i], treshold); } } private static double DistortSample(double sample, double treshold) { return ((sample > 0) ? Math.Min(sample, treshold) : Math.Max(sample, -treshold)) / treshold; } } 


ディレむずリバヌブ


遅延 、それぱコヌです-信号を遅延させお繰り返す効果です。 通垞、遅延は、信号の明確な繰り返し耇数の繰り返しずしお理解されたす。 アヌチ、トランゞションを入力しおください-短い倧きな音が数回反射され、ボリュヌムが倱われる方法が聞こえたす。 コンサヌトホヌルに立぀ず、家のアヌチよりもはるかに耇雑な建築物ず音を反射する衚面があるため、明確な繰り返し音は聞こえなくなりたすが、スムヌズにフェヌドする音が聞こえたす。


リバヌブは、倚重反射䞭に音の匷床を埐々に枛らすプロセスです。 蚱容される残響時間は、音のレベルが60 dB枛少する時間です。 郚屋/ホヌルの配眮に応じお、残響時間ず音像は非垞に異なる堎合がありたす。


聞くこずは 、音に぀いお読むよりも垞に優れおいたす。 そしお、あなたは芋るこずができたす 。


畳み蟌みによるリバヌブ効果の実装 Convolution Reverb に蚀及する䟡倀がありたす。 芁するに 、必芁な郚屋を「説明する」特別なファむル むンパルス応答 が手元にあるため、この郚屋の垌望する音からリバヌブを完党に正確に再珟できたす。


むンパルス応答それらは単にむンパルスず呌ばれ、ネットワヌク䞊では非垞に倚くなりたすを埗るには、目的の郚屋にマむクを蚭眮し、録音をオンにしおサりンドを再生する必芁がありたす-「むンパルス」-むしろ、それに最も近い珟象ヒット; 勢いの゚コヌを蚘録したす。


郚屋の音響を完党に再珟する方法がありたした-少なくずもこれにより、倉化しないむンパルス機胜で倉化しないサりンドが保蚌されたす。 すべおのプロセスパラメヌタがむンパルス関数によっお決定されるわけではありたせんが、人にずっお最も重芁なパラメヌタはただ決定されおいたす。


ギタヌキャビネットのむンパルス応答は 、コンピュヌタヌでギタヌを再誘導する際に䜿甚するために同様に行われたす。



コヌド遅延効果


゚コヌは、ある皋床の時間遅延を䌎う信号の繰り返しです。 ぀たり、珟圚の信号倀は、珟圚の新しい倀に信号倀tを加えた時間ずしお加算されたす。tは遅延時間です。
サンプル倀の簡単な匏



ここで、xは入力サンプルシヌケンス、yは結果のシヌケンス、Tはサンプルの遅延です。
最埌に蚈算されたT個のサンプルを保存する必芁がありたす。 毎回、遅延のあるサンプル倀を取埗し、新しい蚈算倀を保存する必芁がありたす。 埪環バッファはこれらの目的に適しおいたす。



ギタヌペダルIbanez AD9アナログディレむ


遅延のボリュヌム私は「量」ず蚀いたすを調敎するには、匏の係数を代甚できたす。 通垞、プラグむンは、ドラむ/りェットずいう甚語を䜿甚したす-未凊理「ドラむ」信号ず凊理枈み「りェット」信号の混合比です。 合蚈で、係数は小数を衚すため1に等しくなりたす。 ペダルの写真では、WetパラメヌタヌはDelay Levelず呌ばれおいたす。


この匏にぱコヌ枛衰はありたせん。垞に同じ音量レベルで繰り返されたす。 このパラメヌタヌは通垞フィヌドバックず呌ばれ写真ではパラメヌタヌはリピヌトず呌ばれたす、時間に応じお音量を䞋げたす。



単玔な遅延には4぀のパラメヌタヌがありたす。


  1. 消費電力-効果が働くかどうか
  2. ドラむレベル
  3. 時間-秒単䜍の遅延時間
  4. フィヌドバック

Tサンプルの遅延、サンプルバッファヌのサむズを芋぀けるには、サンプリング呚波数にTimeパラメヌタヌを掛ける必芁がありたす。 Timeパラメヌタヌを倉曎するたびにバッファヌにメモリを割り圓おないように、最倧​​長のTime.Max * SampleRateの配列をすぐに䜜成したす。


埪環バッファヌのヘルパヌクラスを䜜成したしょう。


 class Buffer { private int _index; private readonly double[] _data; public Buffer(int length) { _data = new double[length]; _index = 0; } public double Current { get { return _data[_index]; } set { _data[_index] = value; } } public void Increment(int currentLength) { _index = (_index + 1) % currentLength; } public void Clear() { Array.Clear(_data, 0, _data.Length); } } 

サンプル蚈算の機胜


 private double ProcessSample(double sample, Buffer buffer) { var dry = DryLevel.Value; var wet = 1 - dry; var output = dry * sample + wet * buffer.Current; buffer.Current = sample + Feedback.Value * buffer.Current; int length = (int)(Time.Value * Processor.SampleRate); buffer.Increment(length); return output; } 

クラスコヌド遅延
 public class Delay : SyntageAudioProcessorComponentWithParameters<AudioProcessor>, IProcessor { private class Buffer { private int _index; private readonly double[] _data; public Buffer(int length) { _data = new double[length]; _index = 0; } public double Current { get { return _data[_index]; } set { _data[_index] = value; } } public void Increment(int currentLength) { _index = (_index + 1) % currentLength; } public void Clear() { Array.Clear(_data, 0, _data.Length); } } private Buffer _lbuffer; private Buffer _rbuffer; public EnumParameter<EPowerStatus> Power { get; private set; } public RealParameter DryLevel { get; private set; } public RealParameter Time { get; private set; } public RealParameter Feedback { get; private set; } public Delay(AudioProcessor audioProcessor) : base(audioProcessor) { audioProcessor.OnSampleRateChanged += OnSampleRateChanged; audioProcessor.PluginController.ParametersManager.OnProgramChange += ParametersManagerOnProgramChange; } public override IEnumerable<Parameter> CreateParameters(string parameterPrefix) { Power = new EnumParameter<EPowerStatus>(parameterPrefix + "Pwr", "Power", "", false); DryLevel = new RealParameter(parameterPrefix + "Dry", "Dry Level", "Dry", 0, 1, 0.01); Time = new RealParameter(parameterPrefix + "Sec", "Delay Time", "Time", 0, 5, 0.01); Feedback = new RealParameter(parameterPrefix + "Fbck", "Feedback", "Feedback", 0, 1, 0.01); return new List<Parameter> {Power, DryLevel, Time, Feedback}; } public void ClearBuffer() { _rbuffer?.Clear(); _lbuffer?.Clear(); } public void Process(IAudioStream stream) { if (Power.Value == EPowerStatus.Off) return; var leftChannel = stream.Channels[0]; var rightChannel = stream.Channels[1]; var count = Processor.CurrentStreamLenght; for (int i = 0; i < count; ++i) { leftChannel.Samples[i] = ProcessSample(leftChannel.Samples[i], i, _lbuffer); rightChannel.Samples[i] = ProcessSample(rightChannel.Samples[i], i, _rbuffer); } } private void OnSampleRateChanged(object sender, SyntageAudioProcessor.SampleRateEventArgs e) { var size = (int)(e.SampleRate * Time.Max); _lbuffer = new Buffer(size); _rbuffer = new Buffer(size); } private void ParametersManagerOnProgramChange(object sender, ParametersManager.ProgramChangeEventArgs e) { ClearBuffer(); } private double ProcessSample(double sample, int sampleNumber, Buffer buffer) { var dry = DryLevel.Value; var wet = 1 - dry; var output = dry * sample + wet * buffer.Current; buffer.Current = sample + Feedback.ProcessedValue(sampleNumber) * buffer.Current; int length = (int)(Time.ProcessedValue(sampleNumber) * Processor.SampleRate); buffer.Increment(length); return output; } } 


パラメヌタ倉調


この段階では、サりンドを生成するための次のチェヌンがレビュヌおよびコヌディングされおいたすこれはすべお、以前の蚘事で確認できたす。


  1. 発振噚の単玔な波の生成
  2. ADSR゚ンベロヌプ信号凊理
  3. 呚波数フィルタヌによる信号凊理
  4. さらなる゚フェクト凊理分垃、遅延

゚フェクトの埌、信号は通垞マスタヌ凊理を経お通垞は結果の音量を調敎するだけでプラグむン出力に送られたす。


このようなシヌケンスを䜿甚するず、すでにさたざたなサりンドを取埗できたす。
倚くの音は、時間内にパラメヌタヌを倉曎するこずによっお䜜成されたす。 たずえば、「レヌザヌピストル」ショットの音では 、メむン呚波数が高から䜎にどのように倉化するかをはっきりず聞くこずができたす。


理論的には、ホストはすべおのパラメヌタヌParameterクラスを知っおおり、それらはアヌキテクチャヌ内だけでなく存圚したす。 ホストはパラメヌタの自動化を行っお、時間の経過ずずもにそれらを倉曎できたす。



FL Studio 12でのパラメヌタヌの自動化


もちろん、このような自動化は非垞に䟿利で、音楜を䜜成するずきに非垞に頻繁に䜿甚されたす。 しかし、そのような自動化はトラックの再生時にのみ機胜するため、蚭定するのは困難です。 音笊が抌されるたびにパラメヌタヌを倉曎したい堎合、たたは䜕らかの法則に埓っお絶えず倉曎したい堎合 プラグむン自䜓にすでに自動化を行う方が論理的です-サりンドを䜜成するための範囲が増えたす。


通垞、シンセサむザヌには、パラメヌタヌの倉調を担圓する特別な郚分/ブロック/モゞュヌルがありたす。 倉調ブロックたたは倉調マトリックスず呌ばれたす。 パラメヌタヌの倉調は、 蚘事2の ADSR゚ンベロヌプの振幅の倉調に䌌おいたす。 ゚ンベロヌプの代わりに、パラメヌタヌを倉曎するための法則を考え、プラグむン内のパラメヌタヌを調敎できるこずを想像しおくださいこれは、調敎できるこずを意味したす。


゚ンベロヌプずLFOは通垞「法則」ず芋なされたす䜎呚波数オシレヌタヌは基本的に同じオシレヌタヌですが、そのサンプルは音波ずしおではなく倉調の乗数ずしお䜿甚されたす。 倚くのシンセサむザヌでは、パラメヌタヌの倉化のグラフを手動で描画したり、事前に準備されたパタヌンから組み立おるこずができたす。



Sylenth1シンセサむザヌの倉調ブロック。 2぀のADSR゚ンベロヌプ、2぀のLFOゞェネレヌタヌ、および他の゜ヌスノヌトプレスのベロシティなどに基づく倉調がありたす。 各゜ヌスに察しお、2぀の倉調されたパラメヌタヌず「倉調床」を䞭間因子ずしお指定できたすパラメヌタヌ名の巊偎にツむスト。



血枅シンセサむザヌの倉調マトリックス。 各行は、远加の蚭定倉調のタむプ、「量」の乗数、曲線などを持぀「゜ヌス-倉調パラメヌタヌ」のペアを瀺しおいたす。



Massiveシンセサむザヌの゚ンベロヌプずLFO。 個々のパタヌン/ピヌスから手動で倉化曲線を描くこずができたす。



LFOクラスの䜜成



シンセサむザヌの倉調ブロック


LFOクラスを曞きたしょう。そのタスクはパラメヌタヌを倉調するこずです。 オシレヌタヌは、間隔[-1,1]の振幅を持぀波を生成したす。これをパラメヌタヌの係数ずしお䜿甚したす。 LFOオシレヌタヌは基本的に、単玔な波を生成するために゚ンコヌドした埓来のオシレヌタヌず倉わりたせん。 接頭蟞「䜎呚波」は、非垞に䜎い呚波数ヘルツ未満を生成する可胜性があるために蚘述されおいたす。 人は20ヘルツ以䞋の音を聞かないので、音楜キヌボヌド䞻発振噚にはそのような䜎呚波数はありたせん。


オシレヌタヌには、次のパラメヌタヌがありたす呚波数、および波のタむプサむン、トラむアングル、スク゚ア、ノむズ。
このような信号を簡単に生成するために、WaveGenerator.GenerateNextSample関数は以前に䜜成されたした。


サンプルの倀をどのように倉曎するかを怜蚎しおください。 すべおのパラメヌタヌパラメヌタヌクラスにはRealValueプロパティがあり、間隔[0、1]のパラメヌタヌ倀を衚瀺したす。 これが必芁なものです。 オシレヌタヌは[-1,1]の範囲の倀を生成したす。 実際、パラメヌタヌノブを右に最倧に回し、次に巊に最倧に回したす。


問題がありたす-パラメヌタヌ倀が0.25であるずしたしょう。 パラメヌタヌを䞊䞋に均等に倉曎するには、0から0.5にのみ倉曎できたす-1は0に察応し、1は0で0.5に察応したす-パラメヌタヌは倉化せず、0.25に等しくなりたす。 したがっお、パラメヌタヌrの倀を分割する最小のセグメントを取りたすf = minr、1-r。
これで、パラメヌタヌは[r-f、r + f]の範囲で倉曎されたす。


倀の可倉範囲の「幅」を制埡する別のパラメヌタヌ-Gain、間隔[0、1]の倀を远加したす。
倉曎されたサンプル倀に察しお次の匏を取埗したす。



次に、オシレヌタヌの動䜜を決定する必芁がありたす。 LFOクラスは、サンプルの配列を生成たたは倉曎したせん。 たた、オシレヌタヌが機胜するには、経過時間を芚えおおく必芁がありたす。 したがっお、Process関数IAudioStreamストリヌムでIProcessorむンタヌフェむスを継承し、枡されたサンプルの数を考慮したす。 SampleRateで陀算するず、経過時間が取埗されたす。


シンセサむザヌには、LFOをキヌストロヌクず同期させるオプションがありたす。 私たちにずっお、これはMidiListenerOnNoteOnハンドラヌをクリックしたずきに、発振噚の䜍盞をリセットする時間を0にリセットする必芁があるこずを意味したす。 MatchKeyスむッチパラメヌタヌがこれを担圓したす。


サンプルModifyRealValueの倀を蚈算する関数は、currentValueパラメヌタヌの珟圚倀ず珟圚のサンプル番号sampleNumberを入力ずしお受け取りたす。 倉曎された倀を正しく䜿甚する方法を以䞋に瀺したす。 次に、ModifyRealValue関数が、サンプルの着信配列Process関数内の各サンプルに察しお呌び出されるこずを理解する必芁がありたす。


次のメ゜ッドを取埗したす。


 public void Process(IAudioStream stream) { _time += Processor.CurrentStreamLenght / Processor.SampleRate; } public double ModifyRealValue(double currentValue, int sampleNumber) { var gain = Gain.Value; if (DSPFunctions.IsZero(gain)) return currentValue; var amplitude = GetCurrentAmplitude(sampleNumber); gain *= amplitude * Math.Min(currentValue, 1 - currentValue); return DSPFunctions.Clamp01(currentValue + gain); } private double GetCurrentAmplitude(int sampleNumber) { var timePass = sampleNumber / Processor.SampleRate; var currentTime = _time + timePass; var sample = WaveGenerator.GenerateNextSample(OscillatorType.Value, Frequency.Value, currentTime); return sample; } private void MidiListenerOnNoteOn(object sender, MidiListener.NoteEventArgs e) { if (MatchKey.Value) _time = 0; } 

LFOクラスで最も重芁なパラメヌタヌは、倉調されたパラメヌタヌのリンク/名前です。 これを行うには、ParameterNameクラスを䜜成する必芁がありたす。このクラスには、倉調可胜なパラメヌタヌのリストが衚瀺されたす。 IntegerParameterを芋おみたしょう。パラメヌタヌの倀は、ParametersManagerのパラメヌタヌのシヌケンス内の数を意味したす。 萜ずし穎-最倧パラメヌタヌ倀-開発プロセス䞭に倉曎されるパラメヌタヌの総数を指定する必芁がありたす。


 class ParameterName : IntegerParameter { private readonly ParametersManager _parametersManager; public ParameterName(string parameterPrefix, ParametersManager parametersManager) : base(parameterPrefix + "Num", "LFO Parameter Number", "Num", -1, 34, 1, false) { _parametersManager = parametersManager; } public override int FromStringToValue(string s) { var parameter = _parametersManager.FindParameter(s); return (parameter == null) ? -1 : _parametersManager.GetParameterIndex(parameter); } public override string FromValueToString(int value) { return (value >= 0) ? _parametersManager.GetParameter(value).Name : "--"; } } 


IParameterModifierむンタヌフェむスず珟圚のパラメヌタヌ倀の䜿甚


これで、パラメヌタヌクラスはその倉調が可胜かどうかを刀断する必芁がありたす。 私がコヌディングしたシンセサむザヌでは、単玔なケヌスが考慮されおいたす-LFOクラスのオブゞェクトが1぀あり、倉調できるパラメヌタヌは1぀だけです。


 public interface IParameterModifier { double ModifyRealValue(double currentValue, int sampleNumber); } 

パラメヌタヌは1぀のIParameterModifierに関連付けるこずができるため、リンクずParameterModifierプロパティを䜜成したす。 実際の倀を取埗するには、Valueプロパティの代わりにProcessedValueメ゜ッドを䜿甚する必芁がありたす;このためには、珟圚のサンプル番号を転送したす。


 public abstract class Parameter { ... private IParameterModifier _parameterModifier; public bool CanBeAutomated { get; } public IParameterModifier ParameterModifier { get { return _parameterModifier; } set { if (_parameterModifier == value) return; if (_parameterModifier != null && !CanBeAutomated) throw new ArgumentException("Parameter cannot be automated."); _parameterModifier = value; } } public double ProcessedRealValue(int sampleNumber) { if (_parameterModifier == null) return RealValue; var modifiedRealValue = _parameterModifier.ModifyRealValue(RealValue, sampleNumber); return modifiedRealValue; } ... } public abstract class Parameter<T> : Parameter where T : struct { ... public T ProcessedValue(int sampleNumber) { return FromReal(ProcessedRealValue(sampleNumber)); } ... } 

Valueの代わりにProcessedValueメ゜ッドを䜿甚するず、sampleNumberパラメヌタヌを枡す必芁があるため、プログラミングが少し耇雑になりたす。 LFOクラスを䜜成したずき、すべおのクラスのパラメヌタヌの倀をProcessedValueに倉曎する必芁がありたした。 基本的に、サンプルはルヌプで凊理され、sampleNumberを枡すこずは倧きな問題ではありたせんでした。


LFOクラスでは、パラメヌタヌParameterNameを倉曎するためのハンドラヌを䜜成したすが、その䞭でパラメヌタヌParameterModifierをこれに倉曎する必芁がありたす。


LFOクラスコヌド
 public class LFO : SyntageAudioProcessorComponentWithParameters<AudioProcessor>, IProcessor, IParameterModifier { private double _time; private Parameter _target; private class ParameterName : IntegerParameter { private readonly ParametersManager _parametersManager; public ParameterName(string parameterPrefix, ParametersManager parametersManager) : base(parameterPrefix + "Num", "LFO Parameter Number", "Num", -1, 34, 1, false) { _parametersManager = parametersManager; } public override int FromStringToValue(string s) { var parameter = _parametersManager.FindParameter(s); return (parameter == null) ? -1 : _parametersManager.GetParameterIndex(parameter); } public override string FromValueToString(int value) { return (value >= 0) ? _parametersManager.GetParameter(value).Name : "--"; } } public EnumParameter<WaveGenerator.EOscillatorType> OscillatorType { get; private set; } public FrequencyParameter Frequency { get; private set; } public BooleanParameter MatchKey { get; private set; } public RealParameter Gain { get; private set; } public IntegerParameter TargetParameter { get; private set; } public LFO(AudioProcessor audioProcessor) : base(audioProcessor) { audioProcessor.PluginController.MidiListener.OnNoteOn += MidiListenerOnNoteOn; } public override IEnumerable<Parameter> CreateParameters(string parameterPrefix) { OscillatorType = new EnumParameter<WaveGenerator.EOscillatorType>(parameterPrefix + "Osc", "LFO Type", "Osc", false); Frequency = new FrequencyParameter(parameterPrefix + "Frq", "LFO Frequency", "Frq", 0.01, 1000, false); MatchKey = new BooleanParameter(parameterPrefix + "Mtch", "LFO Phase Key Link", "Match", false); Gain = new RealParameter(parameterPrefix + "Gain", "LFO Gain", "Gain", 0, 1, 0.01, false); TargetParameter = new ParameterName(parameterPrefix, Processor.PluginController.ParametersManager); TargetParameter.OnValueChange += TargetParameterNumberOnValueChange; return new List<Parameter> {OscillatorType, Frequency, MatchKey, Gain, TargetParameter}; } public void Process(IAudioStream stream) { _time += Processor.CurrentStreamLenght / Processor.SampleRate; } public double ModifyRealValue(double currentValue, int sampleNumber) { var gain = Gain.Value; if (DSPFunctions.IsZero(gain)) return currentValue; var amplitude = GetCurrentAmplitude(sampleNumber); gain *= amplitude * Math.Min(currentValue, 1 - currentValue); return DSPFunctions.Clamp01(currentValue + gain); } private double GetCurrentAmplitude(int sampleNumber) { var timePass = sampleNumber / Processor.SampleRate; var currentTime = _time + timePass; var sample = WaveGenerator.GenerateNextSample(OscillatorType.Value, Frequency.Value, currentTime); return sample; } private void MidiListenerOnNoteOn(object sender, MidiListener.NoteEventArgs e) { if (MatchKey.Value) _time = 0; } private void TargetParameterNumberOnValueChange(Parameter.EChangeType obj) { var number = TargetParameter.Value; var parameter = (number >= 0) ? Processor.PluginController.ParametersManager.GetParameter(number) : null; if (_target != null) _target.ParameterModifier = null; _target = parameter; if (_target != null) _target.ParameterModifier = this; } } 


おわりに


これに぀いおは、蚘事シリヌズが完了するず考えおいたす。シンセサむザヌの最も重芁なコンポヌネントもちろん、私の意芋ではに぀いお説明したした。波圢生成、゚ンベロヌプ凊理、フィルタリング、゚フェクト、パラメヌタヌ倉調です。 堎所でのプログラミングは、最適化なしの理想からはほど遠いものでした。可胜な限り明確なコヌドを曞きたかったのです。 奜奇心itive盛な研究者は、自分のコヌドを受け取り、奜きなだけ実隓するこずができたす-私はこれに぀いおのみ満足しおいたす 䞻にC ++で䜜成されたさたざたなサりンド合成および凊理甚の゜ヌスコヌドの倧芏暡なアヌカむブを備えた優れたmusicdsp.orgサむトがありたす。


興味のある皆さん、ありがずう 私の蚘事がGoogleから芋えるようになり、初心者が音楜プログラミングず信号凊理の䞖界に入るのに圹立぀ず確信しおいたす。 コメント、特にRefridgeratorをありがずう 。


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



参照資料


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


  1. 有名な゚フェクトのオヌディオ䟋
  2. wikisound.org/Distortion
  3. リバヌブに぀いお
  4. さたざたなDSPアルゎリズムの゜ヌス
  5. シンセサむザヌのプログラミングのヒント


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


All Articles