みなさんこんにちは! C#でのVSTシンセサイザーの作成に関する記事の第4部を読みます。 前のパートでは、信号を生成し、振幅エンベロープと周波数フィルターを適用しました。
今回はディストーションの効果を見ていきます-エレキギタリストやディレイに馴染みのある信号の歪み(エコーでもあります)。
シンセサイザーコンポーネント(ジェネレーター、フィルター、エフェクト)のパラメーターの値を時間内に変更(変調)することで、さまざまな興味深いサウンドを得ることができます。 これを行う方法のオプションを検討してください。
私のシンセサイザーのソースコードはGitHubで入手できます 。
VST GClipプラグインのスクリーンショット
記事のサイクル
- C#WPFでVSTiシンセサイザーを理解して記述します
- ADSR信号エンベロープ
- バタワース周波数フィルター
- 遅延、歪み、およびパラメーター変調
目次
- クリッピング、歪み、オーバードライブ、歪み
- コディム効果の歪み
- ディレイとリバーブ
- コード遅延効果
- パラメータ変調
- LFOクラスの作成
- IParameterModifierインターフェイスと現在のパラメーター値の使用
- おわりに
- 参照資料
クリッピング、歪み、オーバードライブ、歪み
ギターアンプとピックアップの初期モデルはそれぞれシンプルで低品質であり、処理された信号に歪みを加えていました。 アナログアンプを使用する場合、信号の発信音量に応じて信号が歪んでいました。 信号振幅が増加すると、非線形歪みの係数が増加し、さまざまな高調波が追加されます。 家庭用スピーカーを最大限にオンにすると、歪みも聞こえるはずです。
51年目に、Kings of Rhythmのギタリストが途中で損傷したアンプを使用し、プロデューサーがそのサウンドを気に入ったという話があります。
効果「歪み」-英語から「歪み」として翻訳します。 信号の振幅が厳密に制限され始めると、非線形歪みが作成され、新しい高調波が表示されます。 制限(しきい値)が大きいほど、信号はより歪みます。
「ロック」という言葉が含まれるジャンルのほとんどすべてのギターは、ディストーションエフェクトまたはオーバードライブによって処理されます。 有名なエフェクトの音声サンプルへのリンク 。
オーバードライブには、歪みよりも滑らかな振幅制限があります。 オーバードライブは、ソフトクリッピングとも呼ばれ、ハードクリッピングとも呼ばれます。 ギターのオーバードライブは、インディーロック、ポップロックなど、より穏やかなジャンルで使用されています。
歪み(ハードクリッピング)とオーバードライブ(ソフトクリッピング)の効果のおおよその比較
クリッピングとは、0 dBのデジタル振幅を超える場合の不要なアーティファクト(クリック)を指します。 「クリーン」(アナログペダルやプリアンプをエミュレートせずに)歪み信号を実現するエフェクトがあります。 たとえば、 GClipプラグイン(記事の冒頭の画面だけ)は、着信信号を数学的に単純にカットします。
コディム効果の歪み
上記から、本質的に、ハードディストーションは最大絶対振幅値のパラメーター-しきい値によってのみ決定されると結論付けられます。 サンプルの絶対値は1を超えません。つまり、しきい値も間隔[0,1]にあります。
信号を制限するほど(しきい値をゼロに近づける)、ボリュームが弱くなります。 信号のボリュームが変わらないように、元に戻すことができます。サンプルの値をしきい値で除算します。
ハードディストーションの簡単なアルゴリズムを取得します。これは各サンプルに個別に使用されます。
- サンプル値がしきい値より大きい場合は、しきい値と等しくします。
- サンプル値が-Thresholdより小さい場合は、-Thresholdに設定します。
- サンプルの値にしきい値を掛けます。
私たちが書いたシンセサイザーに戻ります( 最初の記事のクラスアーキテクチャのレビュー)。 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つのパラメーターがあります。
- 消費電力-効果が働くかどうか
- ドライレベル
- 時間-秒単位の遅延時間
- フィードバック
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; } }
パラメータ変調
この段階では、サウンドを生成するための次のチェーンがレビューおよびコーディングされています(これはすべて、以前の記事で確認できます)。
- 発振器の単純な波の生成
- ADSRエンベロープ信号処理
- 周波数フィルターによる信号処理
- さらなるエフェクト処理:分布、遅延
エフェクトの後、信号は通常マスター処理を経て(通常は結果の音量を調整するだけで)プラグイン出力に送られます。
このようなシーケンスを使用すると、すでにさまざまなサウンドを取得できます。
多くの音は、時間内にパラメーターを変更することによって作成されます。 たとえば、「レーザーピストル」ショットの音では 、メイン周波数が高から低にどのように変化するかをはっきりと聞くことができます。
理論的には、ホストはすべてのパラメーター(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をこれに変更する必要があります。
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をありがとう 。
すべてに良い!
プログラミングで頑張ってください!
参照資料
以前の記事の記事と本のリストを忘れずに見てください。