指数ADSRエンベロープの方程式のヘルプ


11

アプリケーションコードを使用して、発振器の出力の振幅を整形するための線形ADSRエンベロープを実装しました。アタック、ディケイ、リリースの持続時間とサステインレベルのパラメーターはエンベロープで設定でき、すべてが期待通りに機能します。

ただし、エンベロープのランプ形状を、ほとんどのシンセサイザーがより自然な応答に使用するものに似たものに調整したいと思います。アタックの逆指数関数とディケイとリリースの指数関数です。これらのタイプのランプ形状のエンベロープ出力値を計算するための数式を正しく取得するのに問題があります。線形ランプを計算するために、私は2ポイントフォームを使用して、アタック/ディケイ/サステイン/リリースの入力パラメーター値から派生した開始/終了 /値を接続しています。同じ開始/終了 /ポイント値を使用して、指数(標準および逆)ランプの正しい式を計算できないようです。xx yyxy

上記で説明した線形ランプのアプローチを示すDesmos Graphing Calculatorセッションを保存しまし

誰かが私を正しい方向に向けるのを助けることができれば、それは大いに感謝します。

回答:


10

混乱しているのは、指数関数の減少()が決して0に到達しないため、真に指数関数のセグメントを持つADSRジェネレーターが動かなくなることです。目標値に到達しないためです。たとえば、ジェネレーターがアタックフェーズの高さにあり()、持続値にで着地する必要がある場合、真の指数関数はそこに到達できません。 tは0.5に減衰します。漸近的にのみ0.5になります。 y = 1 y = 0.5exy=1y=0.5

アナログエンベロープジェネレーター(たとえば、誰もが使用している7555ベースの回路)を見ると、アタックフェーズでコンデンサーが充電されているときに、終了を示すために使用されているしきい値よりも「高くなっている」ことがわかります。攻撃フェーズの。+ 15Vから給電される(7)555ベースの回路では、アタックステージ中に、コンデンサは+ 15Vステップで充電されますが、アタックステージは+ 10Vのしきい値に達したときに終了します。これは設計上の選択ですが、2/3は多くの古典的なエンベロープジェネレーターに見られる「マジックナンバー」であり、ミュージシャンがよく知っているものかもしれません。

コンデンサー充電中の異なる「目標比率」に起因するいくつかのADSR形状

したがって、処理する可能性のある関数は指数ではなく、シフト/切り捨て/スケーリングされた関数であり、どのように「押しつぶされる」かについて、いくつかの選択を行う必要があります。

とにかく、なぜこのような数式を取得しようとしているのか知りたいです。おそらく、合成に使用しているツールの制限が原因です。しかし、汎用のプログラミング言語(C、java、python)を使用して、エンベロープのサンプルごとにいくつかのコードを実行し、「状態」の概念を実装する場合は、以下をお読みください。 「このようなセグメントは、0に達したばかりの値から移動します。

エンベロープの実装に関する2つのアドバイス。

最初のものはありませんエンベロープが正確に開始値と終了値に到達するように、すべての勾配/増分をスケーリングしようとします。たとえば、2秒で0.8から0.2になるエンベロープが必要な場合、-0.3 /秒の増分を計算したくなるかもしれません。それをしないでください。代わりに、それを2つのステップに分解します。2秒で0から1.0に進むランプを取得します。次に、0から0.8および1.0から0.2にマップする線形変換を適用します。この方法には2つの利点があります。1つ目は、エンベロープ時間と比較して、0から1へのランプへの計算が簡単になることです。2つ目は、途中でエンベロープパラメーター(増分と開始/終了時間)を変更した場合、すべてが正常に動作することです。シンセの作業をしている場合は、エンベロープデスティネーションのパラメーターをモジュレーションデスティネーションとして設定するように求められるため、この方法が適しています。

2つ目は、事前に計算されたルックアップテーブルをエンベロープ形状とともに使用することです。それは計算的に軽量であり、多くの汚い詳細を排除します(たとえば、指数関数が正確に0に到達しないことを気にする必要はありません-気まぐれでそれを切り捨て、[0、1]にマップされるように再スケーリングします)。また、ステージごとにエンベロープ形状を変更するオプションを提供するのは非常に簡単です。

これが私が説明するアプローチの疑似コードです。

render:
  counter += increment[stage]
  if counter > 1.0:
    stage = stage + 1
    start_value = value
    counter = 0
  position = interpolated_lookup(envelope_shape[stage], counter)
  value = start_value + (target_level[stage] - start_value) * position

trigger(state):
  if state = ON:
    stage = ATTACK
    value = 0  # for mono-style envelopes that are reset to 0 on new notes
    counter = 0
  else:
    counter = 0
    stage = RELEASE

initialization:
  target_level[ATTACK] = 1.0
  target_level[RELEASE] = 0.0
  target_level[END_OF_RELEASE] = 0.0
  increment[SUSTAIN] = 0.0
  increment[END_OF_RELEASE] = 0.0

configuration:
  increment[ATTACK] = ...
  increment[DECAY] = ...
  target_level[DECAY] = target_level[SUSTAIN] = ...
  increment[RELEASE] = ...
  envelope_shape[ATTACK] = lookup_table_exponential
  envelope_shape[DECAY] = lookup_table_exponential
  envelope_shape[RELEASE] = lookup_table_exponential

y =((y2-y1)/(x2-x1))*(x-x1)+ y1の線形スケール/ 2点方程式を取ることで問題を解決したようですが、x変数をeに置き換えて書き直しました^ x to y =((y2-y1)/(e ^ x2-e ^ x1))*(e ^ x-e ^ x1)+ y1。リンクでの私の計算機セッションは、このアプローチを示しています。彼らが私が知っておくべきこれへの落とし穴はありますか?結果は私には正しいようです。
Gary DeReese

これは他のシンセサイザーで見られるエンベロープ形状ではありません。開始レベルと終了レベルの時間/相対位置によっては、非常に線形になる場合があります。
ピシェネット2012年

@pichenettes、これらのエンベロープを生成したスクリプトを貼り付けてもいいですか?
I P

3

これはかなり古い質問ですが、ピシェネットからの回答のポイントを強調したいだけです。

たとえば、2秒で0.8から0.2に変化するエンベロープが必要な場合は[...] 2つのステップに分割します。2秒で0から1.0に変化するランプを取得します。次に、0から0.8および1.0から0.2にマップする線形変換を適用します。

このプロセスは「イージング」と呼ばれることもあり、次のようになります。

g(x,l,u)=f(xlul)(ul)+l

ここで及び下限と上限(ある可能な値である、、および維持レベル)とようなものであり、。これはすでにから範囲にあるため、攻撃フェーズには必要ありません。u 0 1 f x x n 0 1lu01f(x)xn01

これは、このアプローチを使用するために更新された元のDesmosセッションです。ここでは立方体の形状を使用しましたが、が0から1の範囲の出力を生成する限り、任意の形状を使用できます。f(x)

* OPはおそらく長い間使われていないと思いますが、これは他の人を助けるかもしれません。


これをありがとう、私は私が開発者であるDAWのサンプラーをプログラミングしていて、Desmosセッションで提供された数式をプラグインして、それらは完全に機能しました。ラメの線形エンベロープはもうありません!:)
ダグラス

1

ピシェネットのコメントについて、「アタックステージの間、コンデンサは+ 15Vステップで充電されますが、アタックステージは+ 10Vのしきい値に達したときに終了します。これは設計上の選択ですが、2/3は「マジック数」は、多くの古典的なエンベロープジェネレータにあり、ミュージシャンが精通しているものかもしれません。」:

15vの漸近線で10vのターゲットに向けて発射するエンベロープは、実際には線形攻撃を引き起こしています。15Vが簡単に入手できる最高の漸近線であり、線形に十分近いということです。つまり、それについて「魔法」は何もありません。それらは、可能な限り線形になっているだけです。

15Vを使用しているクラシックシンセの数はわかりません。ダイオードのドロップが頻繁に発生するのではないかと思います。私の古いAriesモジュラーは10vエンベロープに13vを使用しており、5vエンベロープに同等に6.5vを使用するCurtis ADSRチップを調べたところです。


1

このコードは、ピシェネットのプロットと同様のプロットを生成するはずです。

def ASD_envelope( nSamps, tAttack, tRelease, susPlateau, kA, kS, kD ):
    # number of samples for each stage
    sA = int( nSamps * tAttack )
    sD = int( nSamps * (1.-tRelease) )
    sS = nSamps - sA - sD

    # 0 to 1 over N samples, weighted with w
    def weighted_exp( N, w ):
        t = np.linspace( 0, 1, N )
        E = np.exp( w * t ) - 1
        E /= max(E)
        return E

    A = weighted_exp( sA, kA )
    S = weighted_exp( sS, kS )
    D = weighted_exp( sD, kD )

    A = A[::-1]
    A = 1.-A

    S = S[::-1]
    S *= 1-susPlateau
    S += susPlateau

    D = D[::-1]
    D *= susPlateau

    env = np.concatenate( [A,S,D] )

    # plot
    tEnv = np.linspace( 0, nSamps, len(env) )
    plt.plot( tEnv, env )
    plt.savefig( "OUT/EnvASD.png" )
    plt.close()

    return env

最後の3つのパラメーター(3つのステージのそれぞれの勾配を決定する)を0と1の間で変化させることをお勧めします。0.5は直線になります。しかし、どうすればいいのかわからない。

また、たとえば1つのステージの長さが0の場合など、すべての使用例を徹底的にテストしていません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.