Ich denke, was Sie verwirrt, ist, dass ein abnehmendes Exponential ( ) niemals 0 erreicht, so dass ein ADSR-Generator mit wirklich exponentiellen Segmenten stecken bleiben würde; weil es niemals den Zielwert erreichen würde. Befindet sich der Generator beispielsweise auf dem Höhepunkt der Angriffsphase (z. B. y = 1 ) und muss er bei y = 0,5 auf einem Sustain-Wert landen , kann er nicht mit einem echten Exponential dorthin gelangen, da das wahre Exponential gewonnen hat. ' t auf 0,5 abfallen, geht es nur asymptotisch auf 0,5!e- xy= 1y= 0,5
Wenn Sie sich einen analogen Hüllkurvengenerator ansehen (zum Beispiel die 7555-basierte Schaltung, die anscheinend jeder verwendet ), können Sie sehen, dass der Kondensator während der Angriffsphase beim Laden "höher zielt" als der Schwellenwert, der zur Anzeige des Endes verwendet wird der Angriffsphase. Auf einer (7) 555-basierten Schaltung, die mit +15 V betrieben wird, wird der Kondensator während der Angriffsphase mit einem + 15-V-Schritt aufgeladen, aber die Angriffsstufe endet, wenn ein Schwellenwert von +10 V erreicht wurde. Dies ist eine Design-Wahl, obwohl 2/3 die "magische Zahl" ist, die in vielen klassischen Hüllkurvengeneratoren zu finden ist, und dies könnte diejenige sein, mit der Musiker vertraut sind.
Daher sind die Funktionen, mit denen Sie sich möglicherweise befassen möchten, keine Exponentialfunktionen, sondern verschobene / abgeschnittene / skalierte Versionen davon, und Sie müssen einige Entscheidungen treffen, wie "gequetscht" sie sein sollen.
Ich bin sowieso neugierig, warum Sie versuchen, solche Formeln zu erhalten - vielleicht liegt es an den Grenzen des Werkzeugs, das Sie für die Synthese verwenden; Wenn Sie jedoch versuchen, diese mithilfe einer universellen Programmiersprache (C, Java, Python) zu implementieren, wobei für jedes Beispiel des Umschlags Code ausgeführt wird und der Begriff "Status" verwendet wird, lesen Sie weiter ... Weil dies immer einfacher ist Drücken Sie Dinge aus wie "ein solches Segment wird von dem Wert, den es gerade erreicht hat, auf 0 gehen".
Meine zwei Ratschläge zur Implementierung von Umschlägen.
Der erste ist nichtum zu versuchen, alle Steigungen / Inkremente so zu skalieren, dass die Hüllkurve genau die Start- und Endwerte erreicht. Sie möchten beispielsweise eine Hüllkurve, die in 2 Sekunden von 0,8 auf 0,2 steigt, sodass Sie möglicherweise versucht sind, ein Inkrement von -0,3 / Sekunde zu berechnen. Tu das nicht. Teilen Sie es stattdessen in zwei Schritte auf: Erhalten einer Rampe, die in 2 Sekunden von 0 auf 1,0 geht; und dann Anwenden einer linearen Transformation, die 0 bis 0,8 und 1,0 bis 0,2 abbildet. Auf diese Weise gibt es zwei Vorteile: Der erste besteht darin, dass die Berechnung im Verhältnis zu den Hüllkurvenzeiten für eine Rampe von 0 auf 1 vereinfacht wird. Das zweite ist, dass, wenn Sie die Hüllkurvenparameter (Inkremente und Start- / Endzeiten) auf halbem Weg ändern, sich alles gut verhält. Gut, wenn Sie an einem Synthesizer arbeiten, da die Leute nach Hüllkurvenzeitparametern als Modulationsziele fragen.
Die zweite besteht darin, eine vorberechnete Nachschlagetabelle mit Umschlagformen zu verwenden. Es ist rechnerisch leichter, es entfernt viele schmutzige Details (zum Beispiel müssen Sie sich nicht mit einem Exponential herumschlagen, das nicht genau 0 erreicht - schneiden Sie es nach Lust und Laune ab und skalieren Sie es neu, damit es auf [0, 1] abgebildet wird). und es ist kinderleicht, für jede Stufe eine Option zum Ändern der Umschlagformen bereitzustellen.
Hier ist der Pseudocode für den Ansatz, den ich beschreibe.
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