有关指数ADSR包络的方程式的帮助


11

通过应用代码,我实现了线性ADSR包络,用于调整振荡器输出的幅度。攻击,衰减和释放持续时间以及延音水平的参数可以在包络上设置,并且一切都会按预期进行。

但是,我想将包络线的斜坡形状调整为类似于大多数合成器用于更自然响应的方式:反指数乘以攻击,指数乘以衰减和释放。我无法正确计算公式来计算这些类型的斜坡形状的包络输出值。为了计算线性斜坡,我使用两点形式,插入从起音/衰减/持续/释放输入参数值得出的开始/结束 / y值。我似乎无法使用相同的起点/终点x / y点值来为指数(标准和反向)斜坡制定正确的公式。xyxy

我保存了一个Desmos Graphing Calculator会话,该会话演示了我上面描述的线性渐变方法。

如果有人能帮助我指出正确的方向,将不胜感激。

Answers:


10

我认为让您感到困惑的是,递减的指数()永远不会达到0,因此具有真正指数段的ADSR生成器将保持停滞状态。因为它永远不会达到目标值。例如,如果生成器处于攻击阶段的高度(例如y = 1),并且必须降落到y = 0.5的维持值,则它不能以真实指数去那里,因为真实指数将不会t衰减到0.5,只会渐近地变为0.5!exy=1y=0.5

如果您看一个模拟包络发生器(例如,每个人似乎都使用基于7555的电路),您会发现在攻击阶段,当电容器正在充电时,它的“目标值”高于用来指示终止的阈值。攻击阶段。在由+ 15V供电的基于(7)555的电路上,在攻击阶段,电容器以+ 15V的步长充电,但是当达到+ 10V的阈值时,攻击阶段结束。尽管2/3是许多经典信封生成器中发现的“魔术数字”,但这是一种设计选择,并且这可能是音乐家熟悉的一个数字。

电容器充电期间由于“目标比”不同而产生的某些ADSR形状

因此,您可能要处理的函数不是指数函数,而是移位/截断/缩放的版本,您将不得不选择如何“压缩”它们。

无论如何,我对于您为什么要尝试获得这样的公式感到好奇-也许是因为您使用的综合工具的局限性;但是,如果您尝试使用通用编程语言(C,java,python)来实现这些代码,并为信封的每个样本运行一些代码,并使用“状态”的概念,请继续阅读...,因为这样做总是更容易表示为“该段将从刚刚达到的任何值变为0”。

关于实施信封的两点建议。

第一个不是尝试缩放所有斜率/增量,以使包络线准确地达到起始值和终止值。例如,您希望信封在2秒内从0.8变到0.2,因此您可能会想计算-0.3 /秒的增量。不要那样做 相反,将其分为两步:在2秒内获得从0到1.0的渐变;然后应用将0映射到0.8和将1.0映射到0.2的线性变换。以这种方式工作有两个优点-首先是它可以简化与包络时间相关的任何计算,使计算从0到1渐变。第二个问题是,如果您在中途更改包络参数(增量和开始/结束时间),一切都会保持良好状态。如果您正在制作合成器,那就很好了,因为人们会要求将包络时间参数作为调制目标。

第二种是使用带有信封形状的预先计算的查找表。它在计算上更轻巧,它消除了许多肮脏的细节(例如,您不必费心不完全达到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的线性比例/两点方程式解决了我的问题,通过用e替换x变量来重写它^ x到y =((y2-y1)/(e ^ x2-e ^ x1))*(e ^ x-e ^ x1)+ y1。我在链接上的计算器会议演示了这种方法。我应该知道他们对此有什么陷阱吗?结果对我来说似乎是正确的。
加里·德里斯

这不是在其他合成器上发现的信封形状。根据开始和结束位置的时间/相对位置,它可能变得非常线性。
pichenettes 2012年

@pichenettes,您是否愿意粘贴生成这些信封的脚本?
P I

3

这是一个很老的问题,但我只想强调一下小象形文字答案中的一点:

例如,您希望信封在2秒内从0.8变到0.2 [...]分成两步:获得在2秒内从0变到1.0的斜坡;然后应用将0映射到0.8和将1.0映射到0.2的线性变换。

此过程有时称为“缓动”,看起来像

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

lu01f(x)xn01

f(x)

*我想OP可能早已不复存在,但这也许对其他人有所帮助。


谢谢您,我正在为我正在开发的DAW编写一个采样器,并插入了Desmos会话中提供的公式,它们运行良好。不再有la脚的线性信封了!:)
道格拉斯

1

关于pichenettes的评论,“在攻击阶段,电容器以+ 15V的步长充电,但是当达到+ 10V阈值时,攻击阶段结束。这是一种设计选择,尽管2/3是“神奇的选择”。数字“可以在许多经典的包络发生器中找到,这可能是音乐家所熟悉的那个。”:

实际上,针对目标为10v的15v渐近线射击的任何信封实际上都会产生线性攻击。仅仅是15v是容易获得的最高渐近线,它足够接近线性。也就是说,这没有什么“魔术”-它们只是尽可能地线性化。

我不知道有多少经典的合成器使用15v,我怀疑通常会有一两个二极管压降。我以前的Aries模块化产品使用13v的10v封装,而我只是查找了Curtis ADSR芯片,该芯片等效地使用6.5v的5v封装。


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

我很感激任何改进,有一件事可能是一个好主意,那就是允许最后三个参数(确定三个阶段中每个阶段的陡度)在0和1之间变化,其中0.5是一条直线。但我看不到如何做到这一点。

另外,我还没有彻底测试所有用例,例如,如果一个阶段的长度为零。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.