帯域制限された波形の生成[終了]
-
05-07-2019 - |
質問
ソフトウェアシンセサイザーを書いていますが、44.1 kHzのサンプルレートでリアルタイムに帯域制限されたエイリアスフリーの波形を生成する必要があります。ノコギリ波は、今のところはうまくいきます。2つのノコギリ波を混ぜ合わせてパルス波を生成できるからです。
これまでのところ、次のアプローチを試しました。
-
起動時に異なる帯域制限周波数で1サイクルの完全に帯域制限された波形サンプルを事前計算し、次に最も近い2つの波形サンプルを混合して再生します。うまくいくと思いますが、非常にエレガントではありません。多くのサンプルが必要か、「ギャップ」それらの間で聞こえます。補間とミキシングもCPUをかなり消費します。
-
一連のDC補償されたsincパルスを統合してノコギリ波を取得します。 DC補正が正確に得られない場合、波がゼロからドリフトすることを除いて、素晴らしいサウンドです(これは非常に難しいことがわかりました)。 DCの問題は、積分器に少し漏れを追加することで軽減できますが、低周波数が失われます。
だから、私の質問は次のとおりです。これを行う通常の方法は何ですか?提案されたソリューションは、多くの音声を一度にリアルタイムで実行する必要があるため、CPUの観点から効率的でなければなりません。
解決
帯域制限された波形生成にアプローチする方法はたくさんあります。通常どおり、計算コストと品質をトレードすることになります。
こちらのサイトをご覧になることをお勧めします:
アーカイブをチェックしてください!良い素材がいっぱいです。キーワード「帯域制限」で検索したところです。少なくとも1週間、忙しい場合に表示される資料。
ところで-それがあなたが探しているものかどうかわからないが、私は数年前にエイリアスを減らした(例えば、本当に帯域制限されていない)波形生成をした。最後のサンプル位置と現在のサンプル位置の間の積分を計算しました。従来のシンセ波形の場合、積分間隔を特異点で分割すると(たとえば、ノコギリ波がリセットされると)、かなり簡単にできます。 CPU負荷は低く、品質は私のニーズに合っています。
同じドリフトの問題がありましたが、積分に非常に低いカットオフ周波数のハイパスを適用すると、その影響がなくなりました。とにかく本物のアナログシンセはサブヘルツ領域に落ちないので、多くを見逃すことはありません。
他のヒント
帯域制限された波形を生成する1つの高速な方法は、帯域制限されたステップ(BLEP)を使用することです。帯域制限されたステップ自体を生成します:
それをウェーブテーブルに保存し、各トランジションを帯域制限されたステップに置き換えて、次のような波形を作成します:
バンド限定サウンド合成のウォークスルーを参照してください。
このBLEPは非因果的であるため(将来に及ぶことを意味します)、リアルタイム波形を生成するには、 MinBLEP は、同じ周波数スペクトルを持ちますが、過去にのみ拡張されます。
MinBLEPはさらにアイデアを取り入れ、 ウィンドウ化されたシンクを取り、実行します 最小位相再構成 結果を統合して保存する 表。オシレーターを作るには それぞれにMinBLEPを挿入するだけです 波形の不連続。だから MinBLEPを挿入する方形波 のこぎりの波形が反転する場所 あなたはMinBLEPを挿入します 値は反転しますが、 通常どおりランプします。
これは、Nilsのアイデアに触発されて思いついたものです。他の人に役立つ場合に、ここに貼り付けます。最後のサンプルからの位相の変化をカーネルサイズ(またはカットオフ)として使用して、ノコギリ波を分析的にフィルタリングします。それはかなりうまく機能し、非常に高いノートでいくつかの可聴エイリアスがありますが、通常の使用では素晴らしい音に聞こえます。
エイリアシングをさらに減らすために、カーネルサイズを少し大きくすることができます。たとえば、2 * phaseChangeでも、最高の周波数は少し失われますが、良い音になります。
また、同様のトピックのSPを参照したときに見つけたもう1つの優れたDSPリソースは次のとおりです。 C ++の合成ToolKit(STK)。これは、多くの便利なDSPツールを備えたクラスライブラリです。帯域制限された波形ジェネレーターを使用する準備も整っています。彼らが使用する方法は、最初の投稿で説明したようにsincを統合することです(私よりもうまくやっていると思いますが...)。
float getSaw(float phaseChange)
{
static float phase = 0.0f;
phase = fmod(phase + phaseChange, 1.0f);
return getBoxFilteredSaw(phase, phaseChange);
}
float getPulse(float phaseChange, float pulseWidth)
{
static float phase = 0.0f;
phase = fmod(phase + phaseChange, 1.0f);
return getBoxFilteredSaw(phase, phaseChange) - getBoxFilteredSaw(fmod(phase + pulseWidth, 1.0f), phaseChange);
}
float getBoxFilteredSaw(float phase, float kernelSize)
{
float a, b;
// Check if kernel is longer that one cycle
if (kernelSize >= 1.0f) {
return 0.0f;
}
// Remap phase and kernelSize from [0.0, 1.0] to [-1.0, 1.0]
kernelSize *= 2.0f;
phase = phase * 2.0f - 1.0f;
if (phase + kernelSize > 1.0f)
{
// Kernel wraps around edge of [-1.0, 1.0]
a = phase;
b = phase + kernelSize - 2.0f;
}
else
{
// Kernel fits nicely in [-1.0, 1.0]
a = phase;
b = phase + kernelSize;
}
// Integrate and divide with kernelSize
return (b * b - a * a) / (2.0f * kernelSize);
}
ブリットからのDCオフセット-シンプルなハイパスフィルターで削減できます! -DCブロッキングキャップを使用する実際のアナログ回路によく似ています!