Я думаю, что вас смущает то, что экспоненциальная убывающая ( ) никогда не достигает 0, поэтому генератор ADSR с действительно экспоненциальными сегментами останется застрявшим; потому что это никогда не достигнет целевого значения. Например, если генератор находится на высоте фазы атаки (скажем, ) и должен приземлиться до значения сустейна при , он не может пойти туда с истинной экспонентой, потому что истинная экспонента выиграла ' т спад до 0,5, он будет асимптотически только до 0,5! у = 1 у = 0,5e−xy=1y=0.5
Если вы посмотрите на аналоговый генератор огибающей (например, схему на основе 7555, которую, кажется, все используют ), вы увидите, что во время фазы атаки, когда конденсатор заряжается, он «стремится выше», чем пороговое значение, используемое для обозначения конца. фазы атаки. На схеме (7) 555, питаемой от + 15В, на этапе атаки конденсатор заряжается с шагом + 15 В, но этап атаки заканчивается, когда достигается пороговое значение + 10 В. Это выбор дизайна, хотя 2/3 - это «магическое число», встречающееся во многих классических генераторах конвертов, и это может быть тем, с кем музыканты знакомы.
Таким образом, функции, с которыми вы, возможно, захотите иметь дело, являются не экспоненциальными, а смещенными / усеченными / масштабируемыми версиями, и вам придется сделать некоторые выборы относительно того, насколько «сжатыми» вы хотите их видеть.
В любом случае мне любопытно, почему вы пытаетесь получить такие формулы - возможно, это из-за ограничений инструмента, который вы используете для синтеза; но если вы пытаетесь реализовать те, которые используют язык программирования общего назначения (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