Je pense que ce qui vous embrouille, c'est qu'une exponentielle décroissante ( ) n'atteint jamais 0, donc un générateur ADSR avec des segments vraiment exponentiels resterait bloqué; car il n'atteindrait jamais la valeur cible. Par exemple, si le générateur est à la hauteur de la phase d'attaque (disons ) et doit atterrir à une valeur de maintien à , il ne peut pas y aller avec une vraie exponentielle, car la vraie exponentielle a gagné '' t décroît à 0,5, il n'atteindra asymptotiquement qu'à 0,5! y = 1 y = 0,5e−xy=1y=0.5
Si vous regardez un générateur d'enveloppe analogique (par exemple le circuit basé sur 7555 que tout le monde semble utiliser ), vous pouvez voir que pendant la phase d'attaque, lorsque le condensateur est en charge, il "vise plus haut" que le seuil utilisé pour indiquer la fin de la phase d'attaque. Sur un circuit basé sur (7) 555 alimenté par + 15V, Pendant la phase d'attaque, le condensateur est chargé avec un pas de + 15V, mais la phase d'attaque se termine lorsqu'un seuil de + 10V a été atteint. Il s'agit d'un choix de conception, bien que 2/3 soit le "nombre magique" que l'on trouve dans de nombreux générateurs d'enveloppes classiques, et c'est peut-être celui que les musiciens connaissent.
Ainsi, les fonctions que vous voudrez peut-être traiter ne sont pas des exponentielles, mais des versions décalées / tronquées / échelles de celui-ci, et vous devrez faire des choix quant à la façon dont elles doivent être "écrasées".
Je suis quand même curieux de savoir pourquoi vous essayez d'obtenir de telles formules - c'est peut-être à cause des limites de l'outil que vous utilisez pour la synthèse; mais si vous essayez d'implémenter ceux qui utilisent un langage de programmation à usage général (C, java, python) avec du code en cours d'exécution pour chaque échantillon de l'enveloppe, et une notion d '"état", lisez la suite ... Parce qu'il est toujours plus facile de exprimer des choses comme "un tel segment passera de la valeur qu'il vient d'atteindre à 0".
Mes deux conseils sur la mise en place des enveloppes.
Le premier n'est paspour essayer de mettre à l'échelle toutes les pentes / incréments afin que l'enveloppe atteigne exactement les valeurs de début et de fin. Par exemple, vous voulez une enveloppe qui passe de 0,8 à 0,2 en 2 secondes, vous pourriez donc être tenté de calculer un incrément de -0,3 / seconde. Ne fais pas ça. Au lieu de cela, décomposez-le en deux étapes: obtenir une rampe qui passe de 0 à 1,0 en 2 secondes; puis en appliquant une transformation linéaire qui mappe 0 à 0,8 et 1,0 à 0,2. Il y a deux avantages à travailler de cette façon - le premier est qu'il simplifie tout calcul que vous aurez par rapport aux temps d'enveloppe à une rampe de 0 à 1; la seconde est que si vous modifiez les paramètres d'enveloppe (incréments et heures de début / fin) à mi-chemin, tout restera bien. Bien si vous travaillez sur un synthé, car les gens demanderont d'avoir des paramètres de temps d'enveloppe comme destinations de modulation.
La seconde consiste à utiliser une table de recherche pré-calculée avec des formes d'enveloppe. Il est plus léger sur le plan des calculs, il supprime de nombreux détails sales (par exemple, vous n'avez pas à vous soucier d'une exponentielle n'atteignant pas exactement 0 - tronquez-la à votre gré et redimensionnez-la afin qu'elle soit mappée sur [0, 1]), et il est très facile de fournir une option pour modifier les formes d'enveloppe, pour chaque étape.
Voici le pseudo-code de l'approche que je décris.
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