Я думаю, что вас смущает то, что экспоненциальная убывающая ( ) никогда не достигает 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
Это довольно старый вопрос, но я просто хочу выделить точку в ответе из пикенетов:
Этот процесс иногда называют «смягчением» и выглядит как
где и - нижняя и верхняя граница (возможные значения , и уровень сустейна), а - что-то вроде . Обратите внимание, что вам не нужно это для фазы атаки, так как она уже колеблется от до .u 0 1 f ( x ) x n 0 1l u 0 1 f(x) xn 0 1
Вот оригинальная сессия Desmos, обновленная для использования этого подхода. Я использовал здесь кубическую форму, но вы * могли бы использовать любую понравившуюся вам форму, если только выдает выходные данные в диапазоне от нуля до одного, а заданные входные данные в диапазоне от нуля до одного.f(x)
* Я полагаю, что ОП уже давно нет, но, возможно, это поможет кому-то еще.
источник
О комментарии пикенет: «На этапе атаки конденсатор заряжается с шагом + 15 В, но этап атаки заканчивается, когда достигнут порог + 10 В. Это конструктивный выбор, хотя 2/3 - это« магия ». число «встречается во многих классических генераторах конвертов, и это может быть тем знакомым музыкантам».
Любой конверт, который стреляет по 15-вольтовой асимптоте с 10-вольтовой мишенью, практически создает линейную атаку. Просто 15v - самая высокая из доступных асимптот, и она достаточно близка к линейной. То есть в этом нет ничего «волшебного» - они просто идут настолько линейно, насколько могут.
Я не знаю, сколько классических синтезаторов используют 15 В - я подозреваю, что часто бывает падение напряжения на диоде или два. Мой старый модуль Aries использует 13 В для огибающей 10 В, и я только что посмотрел на чип Curtis ADSR, который эквивалентно использует 6,5 В для огибающей 5 В.
источник
Этот код должен генерировать сюжеты, похожие на графики Pichenettes:
Я благодарен за любые улучшения, одна вещь, которая может быть хорошей идеей, - позволить последним трем параметрам (которые определяют крутизну каждого из трех этапов) изменяться от 0 до 1, где 0,5 будет прямой. Но я не вижу ничего, как это сделать.
Также я не проверил тщательно все случаи использования, например, если один этап имеет нулевую длину.
источник