Sinus - Speed vs Genauigkeit
-
Hi
Also hab ein paar Sinus Probleme/Fragen!!
Ich programmier grad ein wenig an einem Sinus-generator. Ok ein Generator der einen Sinus erzeugt is ja kein Problem.
Jetzt will ich aber mehrere Sinuskurven überlagern und je mehr man überlagern will, umso knapper wirds mit der Systemleistung, wenn man das alles in echtzeit machen will.Im Moment arbeite ich mit double sin(double) aus math.h, und soweit ich das abschätzen ist das die langsamste Variante, aber halt die genauste.
Hat jemand mit dem Thema Erfahrung und kann mir ein paar Tips geben, denn im Moment macht mein Rechner bei ca 200-300 Überlagerten Kurven schluß.
Ich hab im Faq gelesen float wäre schneller, Ok, aber (ich habs noch nicht ausprobiert) ich nehm an, er wird nicht anähernd doppelt so schnell sein, sonder nur ein paar prozent gut machen.
Die nächste Idee, Sinuswerte in Array vorbrechen. Mmmh Frage: wie viele müsste man da Vorschreiben, wenn man keinen zu hohen Klirrfaktor haben will mit 16bit Auflösung bei 44,1 Khz und frequzenzen von 20hz-20Khz ?? Bekommt man so überhaupt eine brauchbare genauigkeit??
Und was ich noch gefunden hab. einen Quellcode von Audiodsp.org: nennt sich fast Sinus:
Sine calculation Type : waveform generation, Taylor approximation of sin() References : Posted by Phil Burk Notes : Code from JSyn for a sine wave generator based on a Taylor Expansion. It is not as efficient as the filter methods, but it has linear frequency control and is, therefore, suitable for FM or other time varying applications where accurate frequency is needed. The sine generated is accurate to at least 16 bits. Code : for(i=0; i < nSamples ; i++) { //Generate sawtooth phasor to provide phase for sine generation IncrementWrapPhase(phase, freqPtr[i]); //Wrap phase back into region where results are more accurate if(phase > 0.5) yp = 1.0 - phase; else { if(phase < -0.5) yp = -1.0 - phase; else yp = phase; } x = yp * PI; x2 = x*x; //Taylor expansion out to x**9/9! factored into multiply-adds fastsin = x*(x2*(x2*(x2*(x2*(1.0/362880.0) - (1.0/5040.0)) + (1.0/120.0)) - (1.0/6.0)) + 1.0); outPtr[i] = fastsin * amplPtr[i]; }
Der Code schaut ja ganz nett aus, aber was ist besser so eine Aprox. oder das mit der Tabelle, oder gibst noch ne ganz andere Möglichkeit?
Ich mein der Quellcode überschreibt sich damit das er auf 16bit genau ist. Aber könnte man mit der Tabelle ähnliche Resultate erzeugen. Und wie schnell ist der Quellcode im Vergleich zum double sin()?hat hier jemand Erfahrungen gesammelt mit so was? Wichtig ist halt, das der Sinus genau genug wird damit es noch sauber klingt!!
Eigentlich dachte naiv am Anfang, ich könnte so 1000-2000 Sinuse überlagern, aber war wohl weit gefehlt :-(.
Grüße Flow
-
hallo
wie hast du dir das vorgestellt ?
gibst du die verschiedenen sinuse irgendwo ein ? bleiben die dann immer gleich oder aendern sich sie irgendwie ?
Meep Meep
-
du kannst die fpugenauigkeit umstellen auf float, dann könnte es fixer werden.
rapso->greets();
-
Meep Meep schrieb:
hallo
wie hast du dir das vorgestellt ?
gibst du die verschiedenen sinuse irgendwo ein ? bleiben die dann immer gleich oder aendern sich sie irgendwie ?
Meep Meep
Also man soll die andauernt änder können, über eine graphische Balkenanzeige (->Spektrum) und zwar in phase und betrag. Genauso kann man die frequenz der Balken einstellen, die sollte sich aber eher selten ändern. zB wählt man eine Grundfrequentz und will nur Harmonisch vielfache davon oder so .
Float ist sicher schon schneller, aber wahrscheinlich auch nicht so viel schneller. ich werde mal den oben geposteten Aprox.Code ausprobieren und schauen viel schnell der ist und ob des was bringt.
Gruß
-
Flow_cplus schrieb:
Jetzt will ich aber mehrere Sinuskurven überlagern und je mehr man überlagern will, umso knapper wirds mit der Systemleistung, wenn man das alles in echtzeit machen will.
Überlagern = Addieren?
Ich hab im Faq gelesen float wäre schneller, Ok, aber (ich habs noch nicht ausprobiert) ich nehm an, er wird nicht anähernd doppelt so schnell sein, sonder nur ein paar prozent gut machen.
die FPU arbeitet intern sowieso grundsätzlich mit 80 bit und rundet danach ggf. auf 32 oder 64 bit, d.h. die Berechnung ist bei float und double gleich schnell. Bei double müssen mehr Daten herumgeschoben werden, also könnte es daher vielleicht ein wenig langsamer sein. Wie wärs mit nachmessen?
Die nächste Idee, Sinuswerte in Array vorbrechen. Mmmh Frage: wie viele müsste man da Vorschreiben, wenn man keinen zu hohen Klirrfaktor haben will mit 16bit Auflösung bei 44,1 Khz und frequzenzen von 20hz-20Khz ?? Bekommt man so überhaupt eine brauchbare genauigkeit??
Na rechnen wir doch mal. Du willst 44100 Samples pro Sekunde. Die kleinste Frequenz (und damit längste Periode) ist 20 Hz, d.h. du brauchst maximal 44100/20 = 2205 Samples pro Periode. Da der Sinus ziemlich symmetrisch ist, kann man alle Werte aus denen einer Viertelperiode berechnen, bleiben noch rund 550 Samples. Bei 16bit Genauigkeit sind das also astronomische 1100 Bytes *g*
OK die Sache hat natürlich einen Pferdefuß. Alle Frequenzen, die kein ganzzahliges Vielfaches von 20 sind, benötigen Sinuswerte, die nicht in der Tabelle enthalten sind, und müssen interpolierte Werte verwenden. Also machen wir doch einfach die Tabelle größer. Wie wärs mit 20mal so groß? So dass alle ganzzahligen Frequenzen genau sind? => 22000 Bytes. Man kann hier experimentieren.
Natürlich bleibt immer noch die Frage, ob das ganze wirklich schneller ist als die FPU. Messen.
Und was ich noch gefunden hab. einen Quellcode von Audiodsp.org: nennt sich fast Sinus:
Damit kannst du IMHO nichts anfangen, das ist für DSPs, nicht für normale Prozessoren.
BTW glaube ich, dass du die vollen 16bit nicht brauchst, wenn du 1000e von Wellen übereinanderlegst. Dh das bisschen interpolieren dürfte nicht wirklich weh tun. <= Spekulation
-
Bashar schrieb:
Die nächste Idee, Sinuswerte in Array vorbrechen. Mmmh Frage: wie viele müsste man da Vorschreiben, wenn man keinen zu hohen Klirrfaktor haben will mit 16bit Auflösung bei 44,1 Khz und frequzenzen von 20hz-20Khz ?? Bekommt man so überhaupt eine brauchbare genauigkeit??
Na rechnen wir doch mal. Du willst 44100 Samples pro Sekunde. Die kleinste Frequenz (und damit längste Periode) ist 20 Hz, d.h. du brauchst maximal 44100/20 = 2205 Samples pro Periode. Da der Sinus ziemlich symmetrisch ist, kann man alle Werte aus denen einer Viertelperiode berechnen, bleiben noch rund 550 Samples. Bei 16bit Genauigkeit sind das also astronomische 1100 Bytes *g*
OK die Sache hat natürlich einen Pferdefuß. Alle Frequenzen, die kein ganzzahliges Vielfaches von 20 sind, benötigen Sinuswerte, die nicht in der Tabelle enthalten sind, und müssen interpolierte Werte verwenden. Also machen wir doch einfach die Tabelle größer. Wie wärs mit 20mal so groß? So dass alle ganzzahligen Frequenzen genau sind? => 22000 Bytes. Man kann hier experimentieren.
Natürlich bleibt immer noch die Frage, ob das ganze wirklich schneller ist als die FPU. Messen.
Auf diese Art habe ich früher kleine Grafikspielereien optimiert. Das hilft echt viel! Mach das so! Berechne ruhig viele Werte vor, auf ein paar KB kommt es ja heute nicht mehr so an. (natürlich könnte man hier noch anmerken, dass es hilft, wenn das Array in den Datenbereich des Second-Level-Cache des CPUs passt).
Wenn du noch ein bisschen Speed rausholen willst, versuche so wenig wie möglich Kommazahlen zu verwenden. Vielleicht kannst du dich ja damit anfreunden, das Array nicht aus float-Werten sondern aus int- oder __int64-Werten bestehen zu lassen. Einfach beim Vorausberechnen alle sin()-Ergebnisse mit einem konstanten Faktor multiplizieren, so dass der Wertebereich der Integerzahlen möglichst gut ausgenutzt wird. Du verlierst zwar etwas Genauigkeit, das bringt aber Geschwindigkeit. Schließlich hättest du beim Überlagern der Kurven nur nicht Ganzzahloperationen zu tun.
Aber Vorsicht: Kein Zwischenergebnis der Berechnungen darf den Wertebereich deines Integer-Zahlenformats über- oder unterschreiten (Überlauf). Wenn du dann nämlich multiplizierst (evtl. skalierst) werden deine Ergebnisse falsch.Am Ende landest du bestimmt bei Assembler.
-
warp schrieb:
Auf diese Art habe ich früher kleine Grafikspielereien optimiert. Das hilft echt viel! Mach das so!
Hab ich auch. Aber da gabs auch noch keine FPU in handelsüblichen PC-Prozessoren
Ich wär mir nicht so sicher, dass es Ganzzahl bzw. Festkomma-Arithmetik noch bringt.