Помогите с уравнениями для экспоненциальной огибающей ADSR

11

С помощью прикладного кода я реализовал линейную огибающую ADSR для формирования амплитуды выходного сигнала генератора. Параметры для атаки, затухания и продолжительности релиза, а также уровень сустейна могут быть установлены на конверте, и все работает как положено.

Тем не менее, я хотел бы изменить форму рампы оболочки так, чтобы она напоминала то, что большинство синтезаторов используют для более естественного отклика: обратную экспоненту для атаки и экспоненциальную для затухания и выпуска. У меня проблемы с получением правильных формул для расчета выходных значений огибающей для этих типов рампы. Чтобы вычислить линейные линейные изменения, я использую двухточечную форму, добавляя значения начала / конца / которые получены из значений входных параметров атаки / затухания / сустейства / выпуска. Кажется, я не могу выработать правильную формулу для экспоненциального (стандартного и обратного) линейного изменения, используя одинаковые начальные / конечные значения точек / .у х уxyxy

Я сохранил сеанс графического калькулятора Desmos, который демонстрирует подход для линейных рамп, который я описал выше.

Если кто-то может помочь мне указать верное направление, это будет очень цениться.

Гари ДеРиз
источник

Ответы:

10

Я думаю, что вас смущает то, что экспоненциальная убывающая ( ) никогда не достигает 0, поэтому генератор ADSR с действительно экспоненциальными сегментами останется застрявшим; потому что это никогда не достигнет целевого значения. Например, если генератор находится на высоте фазы атаки (скажем, ) и должен приземлиться до значения сустейна при , он не может пойти туда с истинной экспонентой, потому что истинная экспонента выиграла ' т спад до 0,5, он будет асимптотически только до 0,5! у = 1 у = 0,5exy=1y=0.5

Если вы посмотрите на аналоговый генератор огибающей (например, схему на основе 7555, которую, кажется, все используют ), вы увидите, что во время фазы атаки, когда конденсатор заряжается, он «стремится выше», чем пороговое значение, используемое для обозначения конца. фазы атаки. На схеме (7) 555, питаемой от + 15В, на этапе атаки конденсатор заряжается с шагом + 15 В, но этап атаки заканчивается, когда достигается пороговое значение + 10 В. Это выбор дизайна, хотя 2/3 - это «магическое число», встречающееся во многих классических генераторах конвертов, и это может быть тем, с кем музыканты знакомы.

Некоторые формы ADSR, возникающие в результате различного «целевого отношения» во время заряда конденсатора

Таким образом, функции, с которыми вы, возможно, захотите иметь дело, являются не экспоненциальными, а смещенными / усеченными / масштабируемыми версиями, и вам придется сделать некоторые выборы относительно того, насколько «сжатыми» вы хотите их видеть.

В любом случае мне любопытно, почему вы пытаетесь получить такие формулы - возможно, это из-за ограничений инструмента, который вы используете для синтеза; но если вы пытаетесь реализовать те, которые используют язык программирования общего назначения (C, java, python) с некоторым кодом, выполняющимся для каждого образца оболочки, и понятием «состояние», читайте дальше ... Потому что всегда легче выразить вещи как "такой сегмент будет идти от любого значения, которое он только что достиг до 0".

Мои два совета по внедрению конвертов.

Первый непопытаться масштабировать все наклоны / приращения так, чтобы огибающая точно достигла начальных и конечных значений. Например, вы хотите конверт, который идет от 0,8 до 0,2 за 2 секунды, поэтому у вас может возникнуть искушение вычислить приращение -0,3 в секунду. Не делай этого. Вместо этого разбейте его на два шага: получите темп, который идет от 0 до 1,0 за 2 секунды; и затем применяя линейное преобразование, которое отображает от 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
pichenettes
источник
Казалось, я решил свою проблему, взяв мое линейное масштабное / двухточечное уравнение y = ((y2 - y1) / (x2 - x1)) * (x - x1) + y1, переписав его, подставив переменные x в e ^ x к y = ((y2 - y1) / (e ^ x2 - e ^ x1)) * (e ^ x - e ^ x1) + y1. Мой сеанс калькулятора по ссылке иллюстрирует этот подход. Есть ли у них какие-либо ошибки, о которых я должен знать? Результаты кажутся мне правильными.
Гари Дерез
Это не форма конверта, найденная на других синтезаторах. В зависимости от времени / относительного положения начального и конечного уровня, он может стать очень линейным.
pichenettes
@pichenettes, не могли бы вы вставить скрипт, сгенерировавший эти конверты?
P i
3

Это довольно старый вопрос, но я просто хочу выделить точку в ответе из пикенетов:

Например, вы хотите, чтобы конверт с 0,8 до 0,2 за 2 секунды [...] разбивался на два шага: получение рампы с 0 до 1,0 за 2 секунды; и затем применяя линейное преобразование, которое отображает от 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, обновленная для использования этого подхода. Я использовал здесь кубическую форму, но вы * могли бы использовать любую понравившуюся вам форму, если только выдает выходные данные в диапазоне от нуля до одного, а заданные входные данные в диапазоне от нуля до одного.f(x)

* Я полагаю, что ОП уже давно нет, но, возможно, это поможет кому-то еще.

гость
источник
Спасибо за это, я программировал сэмплер для DAW, для которого я являюсь разработчиком, и включил формулы, представленные в сеансе Desmos, и они отлично работали. Нет больше хромых линейных конвертов! :)
Дуглас
1

О комментарии пикенет: «На этапе атаки конденсатор заряжается с шагом + 15 В, но этап атаки заканчивается, когда достигнут порог + 10 В. Это конструктивный выбор, хотя 2/3 - это« магия ». число «встречается во многих классических генераторах конвертов, и это может быть тем знакомым музыкантам».

Любой конверт, который стреляет по 15-вольтовой асимптоте с 10-вольтовой мишенью, практически создает линейную атаку. Просто 15v - самая высокая из доступных асимптот, и она достаточно близка к линейной. То есть в этом нет ничего «волшебного» - они просто идут настолько линейно, насколько могут.

Я не знаю, сколько классических синтезаторов используют 15 В - я подозреваю, что часто бывает падение напряжения на диоде или два. Мой старый модуль Aries использует 13 В для огибающей 10 В, и я только что посмотрел на чип Curtis ADSR, который эквивалентно использует 6,5 В для огибающей 5 В.

Найджел Редмон
источник
1

Этот код должен генерировать сюжеты, похожие на графики Pichenettes:

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 будет прямой. Но я не вижу ничего, как это сделать.

Также я не проверил тщательно все случаи использования, например, если один этап имеет нулевую длину.

Пи
источник