Eu acho que o que o confunde é que um exponencial decrescente ( ) nunca chega a 0; portanto, um gerador ADSR com segmentos verdadeiramente exponenciais ficaria parado; porque nunca alcançaria o valor desejado. Por exemplo, se o gerador estiver no auge da fase de ataque (digamos, ) e precisar atingir um valor de sustentação em , ele não poderá ir para lá com um exponencial verdadeiro, porque o exponencial verdadeiro venceu ' t decair para 0,5, ele só vai assintoticamente para 0,5!e−xy=1y=0.5
Se você olhar para um gerador de envelope analógico (por exemplo, o circuito baseado em 7555 que todo mundo parece usar ), poderá ver que durante a fase de ataque, quando o capacitor está carregando, ele está "mirando mais alto" do que o limite usado para indicar o fim da fase de ataque. Em um circuito baseado em (7) 555, alimentado por + 15V, durante o estágio de ataque, o capacitor é carregado com uma etapa de + 15V, mas o estágio de ataque termina quando um limite de + 10V é atingido. Esta é uma escolha de design, embora 2/3 seja o "número mágico" encontrado em muitos geradores clássicos de envelopes, e talvez seja com isso que os músicos estejam familiarizados.
Portanto, as funções com as quais você pode querer lidar não são exponenciais, mas versões alteradas / truncadas / dimensionadas, e você terá que fazer algumas escolhas sobre o quão "esmagado" deseja que elas sejam.
De qualquer forma, estou curioso para saber por que você está tentando obter essas fórmulas - talvez seja por causa dos limites da ferramenta que você está usando para síntese; mas se você estiver tentando implementar aqueles que usam uma linguagem de programação de uso geral (C, java, python) com algum código em execução para cada amostra do envelope e uma noção de "estado", continue lendo ... Porque é sempre mais fácil expressar coisas como "esse segmento passará de qualquer valor que acabou de chegar a 0".
Meus dois conselhos sobre a implementação de envelopes.
O primeiro não épara tentar dimensionar todas as inclinações / incrementos para que o envelope atinja exatamente os valores inicial e final. Por exemplo, você deseja um envelope que varia de 0,8 a 0,2 em 2 segundos, para que possa ser tentado a calcular um incremento de -0,3 / segundo. Não faça isso. Em vez disso, divida-o em duas etapas: obter uma rampa que varia de 0 a 1,0 em 2 segundos; e, em seguida, aplicando uma transformação linear que mapeia 0 a 0,8 e 1,0 a 0,2. Há duas vantagens em trabalhar dessa maneira - a primeira é que simplifica qualquer cálculo que você tenha em relação aos tempos de envelope para uma rampa de 0 a 1; a segunda é que, se você alterar os parâmetros do envelope (incrementos e horários de início / término) no meio do caminho, tudo permanecerá bem comportado. Bom se você estiver trabalhando em um sintetizador, já que as pessoas solicitarão parâmetros de tempo de envelope como destinos de modulação.
O segundo é usar a tabela de pesquisa pré-calculada com formas de envelope. É computacionalmente mais leve, remove muitos detalhes sujos (por exemplo, você não precisa se preocupar com um exponencial que não atinja exatamente 0 - truncá-lo à sua vontade e redimensioná-lo para que seja mapeado para [0, 1]), e é fácil fornecer uma opção para alterar as formas dos envelopes, para cada estágio.
Aqui está o pseudo-código da abordagem que descrevo.
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