2020/07/08 (2020年07月 のアーカイブ)
ADSR がチョットワカルようになる記事
これまでに ADSR 関係の記事を幾つか書いてきたのだけど、ADSR についてはまだ言いたい事があるのだ。あなたの ADSR の実装、間違ってないだろうか? と。ADSR はシンセを構成するモジュールとしては簡単な方だし、ソフトウェアで実現するのもそれほど難しくはない。が、ここで説明する点は特に勘違いしやすいので注意して欲しい。
ADSR はざっくりと下の図のような説明がされている事が多い。ADSR の概要を説明するにはこれで問題はないのだけど、この図を見て、「完全に理解した。アタックのピークを過ぎた後は、減衰の曲線が Key On の間は Decay で Key Off になると Release に切り替わるんだね」と言う風に理解してしまうとまずい事になる。

次のような設定にした時を考えてみる。
Decay ⇒ 小、Release ⇒ 大、Sustain ⇒ 小。

音の出始めにガツンと強いピークがあり、そのまま消えるのではなく、その後も薄く鳴り続けリリースの消え際も長い、という感じの音だ。これでアタックのピーク近くの瞬間にキーを離してしまったらどうなるか...。

「Key Off になるとディケイからリリースに切り替わる」だと、緑の線のようになりますよね。これに違和感があるのは明らかでしょう。アタックの一瞬だけのはずだった音量でしばらく鳴り続けるのだから。
正解は次の図

紫の線のように KeyOff の後、Sustain レベルまでは Decay の速さ + Release の速さで減衰しないといけない (減衰は漸近線なので、Decay の減衰カーブだけの場合は Sustain レベルに辿り着けないのはわかると思う)。
また Attack のピークに至る以前に Key Off が発生した場合も同様で、次の図のように Key Off 時点の現在値が Sustain レベルより高い間は Decay の減衰 + Release の減衰、現在値が Sustain レベル以下ならば Release の減衰が適用される。


つまり Decay の減衰の発動条件は、
「Attack フェーズ以外で、現在の値が Sustain レベルより高い時」であり、
Release の減衰の発動条件は
「Attack フェーズ以外で、現在のキーの状態が Off の時」なのだ。
そしてこの二つの減衰は重なり合う事もある。
また Attack フェーズとは、
「Key On でトリガーされてから現在値を増加させて行き、ピーク値に到達する、または Key Off になるまで」である。
if( detectKeyPositiveEdge )
AttackPhase = true
if( CurrentValue >= PeakLevel or Key == OFF )
AttackPhase = false
if( AttackPhase ){
increase CurrentValue by AttackRate
}
else{
if( CurrentValue > SustainLevel )
decrease CurrentValue by DecayRate
if( Key == OFF )
decrease CurrentValue by ReleaseRate
}
これだけの事で以前書いた リトリガーの問題 も解決する (そして残念な事に、このように現在の値を基に曲線をトリガーしたり、複数の減衰曲線を重ねたりしようとすると Web Audio API のオートメーション関数ではとても面倒な事になるのだ)。
ハードウェアで作る時は大体参考にする公開された回路図通りに作る事から始めるケースが多いので間違える事は少ないのだが、ソフトウェアでは最初の ADSR の波形を見ながらスクラッチから自分で実装しようとして失敗する場合があるので気を付けて欲しい。
Posted by g200kg : 2020/07/08 21:31:38