Welche Zeitbereiche für Hüllkurven?

amesser

||||
Moin,

ich lass euch mal an meinen Gedanken teilhaben. Bin hier eher als Techniker statt als Musiker unterwegs und versuche mich gerade daran einen Synthesizer im FPGA zu implementieren. Der DX-7 dient so ein bischen als Vorlage. Die Oszillatoren laufen schon, jetzt bin ich an den Hüllkurven dran. Dabei komme ich jetzt so ein bischen an die Grenzen der Genauigkeit der Berechnung. Da tun hier gerade lange Zeiten der einzelnen Stufen von so einer Hüllkurve weh. Ich habe da jetzt mal von einem minimalen Attack von 10µs ausgehend simuliert. Da komme ich bis etwa 400ms Attack, ab da wirds dann zu ungenau. Natürlich könnte ich die Genauigkeit der Berechnung erhöhen, das kostet mich aber viele Resourcen im FPGA. (Der hat DSP Blöcke für 18 Bit, bei höherer Genauigkeit braucht man dann gleiche viel mehr davon)

Jetzt meine Frage an die Musiker: Welche Zeitbereiche sind denn für die Hüllkurvensegmente sinnvoll? Wenn ich z.B. erst bei 50µs starte, dann treten die Rechenfehler erst ab 2.5s auf. Ich habe mal verschiede Anleitungen von aktuellen Synths gewälzt, aber da stehen meist keine konkreten Zahlen für die Hüllkurven drinnen. Eine weitere Möglichkeit wäre es, dass ich eine (wählbare) Untersetzung der Rechenschritte einbaue. Das entspricht dann so ungefähr dem Kippschalter den man auf vielen Eurorack ADSRs findet. Das hätte dann halt die Folge, das bei langen Zeitbereichen die Kurve möglicherweise "stufig" umschaltet. Allerdings, im Moment wird alle 7µs ein neuer Hüllkurvenpunkt gerechnet, bei einer Untersetzung 1:100 wären das immer noch alle 0.7ms ein neuer Punkt, das hört man dann wohl eher nicht. und ich käme bis auf 50s hoch.

Was meint Ihr? Schonmal danke für den Input. (Und ja meine Hüllkurven sind im Moment so schnell, das die eigentlich Audio sind, wohl vollkommen übertrieben, aber der Döpfer kann auch 20µs)

Grüße,
Andreas
 
nach unten sind genaue werte super, bei 400ms braucht das kein mensch mehr, da kannst du 10ms schritte haben wenn es nicht anders geht.

ich skaliere GUI controls / parameter für attack zeiten schon mal mit log90.
 
Zuletzt bearbeitet:
aber der Döpfer kann auch 20µs
Und das ist auch gut so. Je schneller, desto besser. Und ja, das kann man wirklich hören. Es war ein Kritikpunkt von mir beim direkten Vergleich der digitalen Sound-Force-Hüllkurven mit den analogen Doepfer-Modulen. Wenn Du es schaltbar machen kannst, sind langsamere Zeiten auch okay. Ansonsten - mache es so schnell, wie es digital eben möglich ist.
 
Ein Sample bei 44.1kHz Abtastrate dauert 23µs. Natürlich könnte man darunter auch noch rechnen, das wäre aber mit Sicherheit nicht mehr wahrnehmbar. Vermutlich ist eine Stufigkeit zwischen 50 und 100µs auch nicht wirklich wahrzunehmen.

Aber ein maximaler Attack von 2.5s bei 50µs oder 5s bei 100µs ist für einige Sounds einfach zu wenig. 20s ist wohl eher ein üppiger Endwert.
 
Kommt wohl auch sehr auf die Frequenz des Grundtons an?
 
Ein Sample bei 44.1kHz Abtastrate dauert 23µs. Natürlich könnte man darunter auch noch rechnen, das wäre aber mit Sicherheit nicht mehr wahrnehmbar.

genau, auf das hörbare spektrum kommt es an. bei 1/44100 kannst du bereits problemlos zwischen zwei samples interpolieren, ohne dass sich das geräusch des knackens großartig ändert.
 
Danke für die Rückmeldungen. Manchmal hilft es ja schon, einfach mal drüber zu sprechen. Die Idee mit der Untersetzung kam mir auch erst beim Schreiben. Ich denke so werde ich das auch erstmal umsetzen. Bei schnellem Attack wird für jedes Sample die Hüllkurve neu gerechnet, bei langsamen Attack nur jedes x'te Sample. (x wird 1...64) Das Ganze versteckt sich dann ja eh hinter der Bedienoberfläche, Bei langsamen Attack ist die Zeiteinstellung dann nur etwas grober. (Wenn man bei ~1ms überhaupt von grob sprechen kann) Die interne Samplerate liegt aktuell übrigens bei krummen 162kHz damit ich die 18 bit voll aunutzen kann.
 
nur mal so. ganz einfach. 8 bit signed, 0-255.

den parameterwert mit sich selbst multiplizieren, das gedachte komma zwei stellen nach links verschieben.

0.01
0.04
0.09
0.16
0.25
0.36
0.49
0.64
0.81
1.
1.21
1.44
1.69
1.96
2.25
2.56
2.89
3.24
3.61
4.
4.41
4.84
5.29
5.76
6.25
6.76
7.29
7.84
8.41
9.
9.61
10.24

[...]

501.76
506.25
510.76
515.29
519.84
524.41
529.
533.61
538.24
542.89
547.56
552.25
556.96
561.69
566.44
571.21
576.
580.81
585.64
590.49
595.36
600.25
605.16
610.09
615.04
620.01
625.
630.01
635.04
640.09
645.16
650.25


erster schritt 0.03 groß, mittlerer 2.55, letzter 15.19
 
0.01 und 650.25 passt aber nicht gleichzeitig in 8bit signed, egal wie man skaliert. ADSR Envelopes berechnet man normalerweise nach der Formel x(n+1) = a + b *x(n). b ist dabei sehr nahe an der "1"~ 0.99... Umso länger der Attack, umso näher kommt "b" der eins. Das Problem hierbei ist, das x eben eine feste Breite hat und wenn b nur nah genug an 1 herankommt, dann ist irgendwann das Ergebnis von b*x(n) == x(n) (weil das Ergebniss ja wieder auf die Bit--Breite von x skaliert werden muss) und dann funktioniert die Integration nicht mehr, da kann dann alles mögliche passieren. Mit Fließkomma ist die Multiplikation weniger das Problem weil man da ja noch den Exponenten hat und man große Dynamik abdecken kann, hier treten die Fehler erst bei der Addition auf. Wenn die Größenordnungen der Summanden zu stark abweichen verliert man die Präzission der kleineren Summanden. (Aber selbst 32 Bit Fließkomma hat 24 Bit Mantisse und damit schon mehr als ich nehmen will/kann)
 
ich habe nur der lesbarkeit halber float bzw reelle zahlen benutzt - wie du das bei dir errechnen und repräsentieren kannst oder musst, keine ahnung. wenn du da max. 18 bit hast böte sich fixed point an.

das skalieren oder verzerren von wertebereichen ist jedenfalls der einfachste weg eine generell höhere auflösung zu umgehen, wenn man diese höhere auflösung nur an einem ende braucht. dass das bei int zu int nicht immer einfach ist, ist klar.
man könnte auch die unteren 8 bit in 0.1er schritte umwandeln und die obere hälfte des wertebereichs macht dann halt plötzlich 5er oder 10er schritte.

dass es "normale" formeln gibt mit denen man üblicherweise hüllkurven berechnet dürfte ein gerücht sein, du kannst dazu jedes beliebige verfahren verwenden was dir gerade einfällt.
vielleicht hast du da auch speicher? wo du ein lookup table reinschreiben kannst?
 
wenn b nur nah genug an 1 herankommt, dann ist irgendwann das Ergebnis von b*x(n) == x(n)
Das ist nunmal die Crux der begrenzten Wortlänge. Und je höher die Samplerate, desto früher kommt man an die Grenze.

Aber Deine Lösung ist natürlich sehr gut und völlig ausreichend, denn wenn b immerhin so weit weg von 1 ist, dass b*x(n) sich von x(n) sicher unterscheidet, so ist doch der resultierende Amplitudensprung sehr gering und auch dann nicht hörbar, wenn er nur alle z.B. 32 Samples upgedatet wird.

PS.: Ich beibe dabei: genauer als 50µs ist overkill.
 
Erstmal noch Danke für die Rückmeldungen. Leider ist die Sache die letzten beiden Wochen wegen was anderem liegen geblieben. (Ich hatte mich mit mit dem Auslesen von Tier-RFID Tags beschäftigt. Da bin ich jetzt aber erstmal an meine Grenzen gekommen was Analogtechnik betrifft, ich wollte so einen Leser komplett selbst bauen, das ist aber komplizierter als man denkt, also wenn man mehr als 2cm Reichweite will :) )

Also im Moment will ich mich auf die 18 Bit beschränken, das ganze dann als Fixpunktzahl mit Wertebereich 0 bis 0.99.. (Q18) Mal sehen wie weit ich damit komme. Die Idee mit der unterschiedlichen Skalierung ist auch interessant. Was mir gerade beim schreiben einfällt wäre noch eine dynamische Untersetzung, am Anfang wo der Anstieg groß ist berechnet man für jedes Sample neu, um so näher man dem Zielwert kommt um so stärker untersetzt man. Möglicherweise reicht dann sogar eine Addition ohne Multiplikation und man bekommt trotzdem den typischen Verlauf hin.

vielleicht hast du da auch speicher? wo du ein lookup table reinschreiben kannst?
Ja schon. Allerdings nicht genug für eine 18 Bit Lookuptable, müsste man also irgendwie etwas quantisieren. Ich versuchs erstmal algorithmisch zu machen. Hintergrund: Ich möchte eigentlich möglichst viel des Speicher aufheben, damit ich später auch anderen Wellenformen bzw genauer: Wavetables ablegen kann. Zu Beginn will ich erstmal nur 6 OP FM machen. Von da ist es zum 6-Oszillator Subtraktiven Synth aber nicht wirklich weit. Das wäre dann mein nächstes Ziel. D.h. es kommen dann noch 1-2 Filter hinter den FM dahinter. Danach würden dann einfache Wavetables kommen also zwei Wellenformen überblendbar. Am Ende kann das Teil das alles gleichzeitig. Mein Kopf ist so voller Ideen dafür, dass ich eigentlich meinen Job kündigen müsste, auch weil mich das Thema total interessiert. Die Platform auf der ich das mache hat schon echt Power, da ist viel Raum. Neben dem FPGA ist da auch noch ein Quad-Core 64 Bit RISC-V 625MHz drauf, das Eval Board hat dazu noch 1GB DDR4 RAM.

Mal sehen wie viel davon am Ende wirklich steht. Ich habe leider immer viel zu viele Ideen für zu wenig Zeit und das restliche Leben will ja auch noch organisiert werden. Das Grundrauschen im meinem Kopf ist einfach zu hoch. Egal. Wenn ich die anderen liegengeblieben Sachen aufgeholt habe (Hier liegen noch zwei Envelopes zum Löten rum) mache ich dann endlich mal mit dem FPGA weiter.
 
Hallo Andreas,
ich habe auch einen Synthesizer im FPGA entwickelt (siehe hier), sogar mit recht begrenzten Ressourcen.
Mein Envelopegenerator berechnet die Envelope-Werte nicht nach einer Formel, sondern ist salopp gesagt eigentlich nur ein Zähler mit einstellbarer Schrittweite. Ja klar, da steckt schon mehr dahinter.
Vielleicht solltest du dich von dem Konzept der Berechnung des Wertes mit den erforderlichen Kompromissen (Genauigkeit vs. Envelope-Länge) einfach verabschieden und auch über ein Zähler-Konzept nachdenken.
Beispiel: Der Envelopegenerator meines Synthi generiert insgesamt 12 Envelopes mit je 8 Phasen für eine Sample-Frequenz von 96kHz. Zeitbasis ist 1 Mikrosekunde, jede Phase hat 1000 Schritte, ein Schrittlänge ist minimal eins und maximal 524288 (Zählerbreite 19 Bits), d.h. eine Phase ist minimal 1 Millisekunde und maximal 524 Sekunden lang. Für die Zeitsteuerung braucht man also nur Zähler, keine Multiplikationen oder gar Divisionen. Bei der Berechnung des aktuellen Hüllkurvenwertes (in Abhängigkeit vom Zielwert und Zeitpunkt) wird dann multipliziert und dividiert (alles mit Hardware-Makros, Multiplikation mit 18x18 Bits und Division mit 24/12 Bits Wortbreite). Da werden dann auch dynamische Einflüsse wie Velocity mit reinberechnet.
Das ganze ist natürlich skalierbar, aber zeigt die Größenordnung auf.

Wir können und gerne dazu austauschen.

Grüße,
Dirk
 
Zuletzt bearbeitet:


News

Zurück
Oben