Aide avec les équations pour l'enveloppe ADSR exponentielle

11

Avec le code d'application, j'ai mis en place une enveloppe ADSR linéaire pour façonner l'amplitude de la sortie d'un oscillateur. Les paramètres d'attaque, de déclin et de durée de relâchement ainsi que le niveau de sustain peuvent être définis sur l'enveloppe et tout fonctionne comme prévu.

Cependant, je voudrais ajuster les formes de rampe de l'enveloppe à quelque chose qui ressemble à ce que la plupart des synthétiseurs utilisent pour une réponse plus naturelle: exponentielle inverse pour l'attaque et exponentielle pour la décroissance et la libération. J'ai du mal à obtenir mes formules pour calculer les valeurs de sortie d'enveloppe pour ces types de formes de rampe. Pour calculer les rampes linéaires, j'utilise la forme à deux points, en branchant les valeurs de début / fin / qui sont dérivées des valeurs des paramètres d'entrée d'attaque / décroissance / maintien / libération. Je n'arrive pas à trouver la bonne formule pour les rampes exponentielles (standard et inverses) en utilisant les mêmes valeurs de point de début / fin / .y x yxyxy

J'ai enregistré une session Desmos Graphing Calculator qui illustre l'approche pour les rampes linéaires que j'ai décrite ci-dessus.

Si quelqu'un peut m'aider à me diriger dans la bonne direction, ce serait très apprécié.

Gary DeReese
la source

Réponses:

10

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,5exy=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.

Certaines formes ADSR résultant de différents "rapports de visée" pendant la charge du condensateur

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
pichenettes
la source
J'ai semblé résoudre mon problème en prenant mon échelle linéaire / équation à deux points de y = ((y2 - y1) / (x2 - x1)) * (x - x1) + y1, en le réécrivant en remplaçant les variables x par e ^ x à y = ((y2 - y1) / (e ^ x2 - e ^ x1)) * (e ^ x - e ^ x1) + y1. Ma session de calculatrice au lien illustre cette approche. Y a-t-il des problèmes avec ça que je devrais être au courant? Les résultats me semblent corrects.
Gary DeReese
Ce n'est pas une forme d'enveloppe que l'on trouve sur d'autres synthétiseurs. Selon le temps / la position relative du niveau de début et de fin, il peut devenir très linéaire.
pichenettes
@pichenettes, seriez-vous prêt à coller le script qui a généré ces enveloppes?
P i
3

C'est une assez vieille question, mais je veux juste souligner un point dans la réponse des pichenettes:

Par exemple, vous voulez une enveloppe qui passe de 0,8 à 0,2 en 2 secondes [...] décomposez-la en deux étapes: obtenir une rampe qui va de 0 à 1,0 en 2 secondes; puis en appliquant une transformation linéaire qui mappe 0 à 0,8 et 1,0 à 0,2.

Ce processus est parfois appelé «assouplissement» et ressemble à

g(x,l,u)=f(xlul)(ul)+l

où et sont les bornes inférieure et supérieure (les valeurs possibles étant , et le niveau de maintien) et est quelque chose comme . Notez que vous n'en avez pas besoin pour la phase d'attaque car elle va déjà de à .u 0 1 f ( x ) x n 0 1lu01f(x)xn01

Voici la session Desmos originale, mise à jour pour utiliser cette approche. J'ai utilisé une forme cubique ici, mais vous * pouvez utiliser la forme que vous voulez, tant que produit des sorties allant de zéro à une entrée donnée allant de zéro à une.f(x)

* Je suppose que l'OP a probablement disparu depuis longtemps, mais peut-être que cela aide quelqu'un d'autre.

Client
la source
Merci pour cela, je programmais un échantillonneur pour un DAW dont je suis développeur, et j'ai branché les formules fournies dans la session Desmos et elles ont parfaitement fonctionné. Plus d'enveloppes linéaires boiteuses! :)
Douglas
1

À propos du commentaire de pichenettes, "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. C'est un choix de conception, bien que 2/3 soit la" magie nombre "trouvé dans de nombreux générateurs d'enveloppes classiques, et c'est peut-être celui que les musiciens connaissent.":

Toute enveloppe qui vise une asymptote 15v avec une cible 10v crée pratiquement une attaque linéaire. C'est juste que 15v est l'asymptote la plus élevée disponible facilement, et il est assez proche de linéaire. Autrement dit, il n'y a rien de "magique" à ce sujet - ils vont juste pour aussi linéaire que possible.

Je ne sais pas combien de synthés classiques utilisent le 15v - je soupçonne qu'il y a souvent une chute de diode ou deux. Mon ancien modulaire Aries utilise du 13v pour une enveloppe de 10v, et je viens de chercher la puce Curtis ADSR qui utilise, de manière équivalente, 6,5v pour une enveloppe de 5v.

Nigel Redmon
la source
1

Ce code devrait générer des tracés similaires à ceux des 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

Je suis reconnaissant pour toute amélioration, une chose qui peut être une bonne idée est de permettre aux trois derniers paramètres (qui déterminent la pente de chacune des trois étapes) de varier entre 0 et 1, où 0,5 serait une ligne droite. Mais je ne vois pas comment le faire.

De plus, je n'ai pas testé à fond tous les cas d'utilisation, par exemple si une étape a une longueur nulle.

P i
la source