Hi,
Ich glaube hier bin ich doch gut aufgehoben mit meinem Problem :)
Nun ich habe mal vor längerer Zeit eine FFT in C++ programmiert als ich
dabei war die FFT zu "begreifen". Diese funktioniert schlussendlich auch
wie sie das tun sollte und ich habe auch was dazugelernt :) Gut das zum
1. Schritt, jetzt möchte ich nach längerer Zeit wieder mal was DSP
mäsiges machen und habe die Idee einen Low-Pass filter zu schreiben.
Was ich bisher dazu gemacht hatte ist:
1. Das reelle Signal (ein meinem Fall eine Audio Datei) laden und dabei
von 32bit int zu double konvertieren.
2. Das Signal in x Stücke aufteilen > (file-länge-in-samples / 1024).
Wobei meine FFT 2048 Samples groß ist. Also somit immer die hälfte der
FFT füllen. Da Signal halt nur reell > nur in RE. IM Werte bleiben auf
0.
3. Komplexe FFT durchführen.
4. Konvertiere aus den Arrays RE und IM in Polarform. Resultat dann in
Arrays Ampl und Phase. Lösche RE und IM > buffer clear.
Dann mittels Schleife von 0 bis max. gewünschter Freq. von Polar wieder
zurück in RE und IM. Negative Frequenzen werden mit einbezogen. Alle
Frequenzen oberhalb von MaxFreq sind nun auf 0 und somit weg.
Die Frequenzen wurden vorher von Hz zu Array-Index berechnet, also auf
die FFT länge berechnet mittels Index = (Frequenz / (maxFreq/fftsize))
maxFreq = 44100 in meinem Fall / fftsize halt wie oben gesagt 2048.
Ok, dann zum 5 Schritt, Komplexe IFFT > 1024 Samples aus RE > zu 32Bit
int > in andere Datei sequenziell speichern.
Wieder zu Punkt 1, solange bis alle Parts abgearbeitet wurden.
Was passiert wenn ich die Datei nun lade und abspiele ist, das leakages
entstehen im Frequenz-Spektrum. Das hört sich an wie ein "Zappen" und
hängt immer oberhalb der max. Freq die noch vorhanden ist nach dem
Filtern.
Im Zeitbereich sind das einfach nur heftige Sprünge zwichen jeden Frames
die bearbeitet wurden, halt jede 1024 Samples springt er steil nach oben
oder unten = also Signal ist zerhackt.
Meine Frage, warum passiert das mit dem "zerhacken" warum ist das Signal
nicht weich in den Übergängen? Ich habe auch schon versucht, die anderen
restlichten 1024 Samples zu "überlappen", also overlap-add Prinzip, kann
man aber vergessen bringt nix, Problem besteht weiterhin.
Ist das Low-Passen so Schwer? Hab aber auch nix Sinnvolles im Netz
gefunden, da ich einerseits nicht die Fachbegriffe für mein exaktes
Problem finden kann. Das Overlapp Add ist mir klar, wird zur fast
convolution per DFTs verwendet. Meine Anwendung macht ja das gleiche,
halt lowpassen per FFT Faltung, nur das ich keinen direkten Kernel (als
file oder so) habe mit dem ich dann im Freq-Bereich multiplizieren
würde, sondern halt nur die Frequenzen grob auf null setze = Rectangular
Frequenz Filtern was ja in einem Sync (Zeit bereich) resulierten
müsste!?
Anyway, viellecht weis ja jemand um Rat oder einen Tip wie man das mit
kleinen fft-lowpassen gescheit macht.
Hab halt nur die Möglichkeit ne FFT zu wählen, die größer ist als die
Datei (von den Samples her) und dann an einem Stück zu low-passen. Macht
aber keinen Sinn bei files > kritische Grenze Gigabytes :S
Ne will das schon gescheit machen mit kleinen FFTs.
Lieber frage ich hier bevor ich noch 100 Jahre nach ner Lösung suche ...
Habe ein Screennshot angehängt wo man das Problem gut sehen kann.
Ich bedanke mich für Hilfe und Durchlesen :)
Gruß, zerall :D
> 1. Schritt, jetzt möchte ich nach längerer Zeit wieder mal was DSP
mäsiges machen und habe die Idee einen Low-Pass filter zu schreiben.
Soweas macht man mit einem IIR- oder FIR-filter.
Das Ganze mittels kurzer Sequenzen über FFT-Filterung zu berechnen ist
unsinnig. Wollest du nur wissen welche Schlechtigkeiten mit einer
unpassenden Methode auftreten?
Das ist eben das Problem der FFT. Sie ist nicht wirklich
amplitudengenau, damit erreichst Du nur eine ungefähre Rekonstruktion
des Originals.
Mit welcher Funktion hast Du gefenstert? Probiere mal ein Kaiser7.
Zudem musst Du die Übergänge smoothen. Dazu benötigst Du zwei interlaced
geschachtelte FFTs und anschliessende iFFTs. Den Übergang / Überblendung
zwischen den beiden iFFT-Syntheseausgängen bildest Du mit einem
Integral-S oder einer Sinusfunktion. Damit entfallen die unstetigen
Übergänge und Du bekommst wieder eine halbwegs echte Rekonstruktion.
Bei Formant-freien Audio-Sample Synthesen, wird das so gemacht.
Hi,
Erstmal Danke für die Antworten!
> Soweas macht man mit einem IIR- oder FIR-filter.
Hatte ich auch schon versucht, war leider zu langsam (auf der höchsten
Compiler Stufe mit sse2 usw).
>Wollest du nur wissen welche Schlechtigkeiten mit einer unpassenden Methode
auftreten?
So weit bin ich noch nicht in die Materie vorgestoßen um das entscheiden
zu können :)
> Mit welcher Funktion hast Du gefenstert?
Wenn ich fenstere (hatte einen Nuttall verwendet) hört sich das Signal
Amplituden-Moduliert an, also wobbelig da ja am anfang eingeblendet und
am Ende ausgeblendet wird, und somit AMs enstehen + das leakage
weiterhin. =(
Somit habe ich keine Fenster Funktion momentan drinn.
Das mit dem überblenden der Übergänge finde ich jetzt interessant, damit
könnte es zumindest "smoother" werden stimmt! Wie müsste ich da
überblenden, sprich wenn ich auch eine Fensterfunktion reinhaue, ab
welcher Position muss ich quasi überlappen (Addieren), z.B wenn ich
jetzt 1024 Samples habe, eine 2048 FFT und ein Low-Pass z.B ab 1500Hz.
Wie kann ich zwischen den steilen Veränderungen gescheit interpolieren?
Gruß, zerall
> Hatte ich auch schon versucht, war leider zu langsam> (auf der höchsten Compiler Stufe mit sse2 usw).
War zu langsam? Das war jetzt ein kleiner Scherz von dir.
Also dein PC schafft locker eine 1MHz-Datenrate.
Vielleicht machst du ja was mit deinem C++-Programm falsch. Erzeugst du
mit jedem Datenwort ein "new"?
Hi,
Ich habe damals einen Sync-Filter erzeugt, dazu hab ich ne Funktion
verwendet um den Sync so anzupassen, dass er ab einer bestimmen Frequenz
cutted + Hann Filter für das smooth-ing des Filters.
Den Code für den FIR-Filter habe ich leider nicht mehr, aber ich habe
damals vor der Verarbeitung der n * m Durchläufe alles auf den heap
allociert.
Damals hatte ich auch zuerst die Datei > Samples in den Speicher
geladen, so dass auch keine HDD Zugriffe enstehen könnten, zumindest
keine unoptimierten. (beim Testen lade ich meist alles in den Speicher)
Innerhalb der Schleifen wurde nichts außer der Multiplikationen und
Summierung durchgeführt. Damit ich keine Range-Checks durchführen musste
in der Schleife, habe ich dieses Problem auch zuvor mit 0-Padding
gelöst.
Als Datentyp hatte ich nen float verwendet + sse2 option.
(Btw: ist das auch irgendwie mit int's vernünftig durchführbar ohne dass
ich wegen den Großen Werten Clippe?, vielleicht wäre es ja dann
schneller)
Compiliert hatte ich das mit Visual Studio 2010 (die Test Version mit
allen Optionen freigeschaltet) und trozdem war es leider zu langsam. Der
Kernel war auch nicht sehr klein, da ich nur alle Frequenzen von 0-100Hz
haben wollte.
Aber abgesehen davon hab ich soeben einen Fortschritt gemacht!!
Ich habe nun einfach anstatt 1024 Samples in die FFT zu laden etwas
mehr, also um genau zu sein 3 * 1024 = 3072 Samples geladen, also den
benachbarten Bereich, dann den low-pass durchgeführt, dann halt nur die
1024 S. aus der Mitte dieser 3072 S. rausgelesen. Durch die benachbarten
Samples wirkt das Ergebnis nun Smoother, als Window verwende ich immer
noch einen Nuttall. Und die Window Funktion kann somit auch seine
Wirkung besser erfüllen, da ich ja nicht mehr den vom Ein/Ausblenden
betroffenen Bereich verwerte, sondern die Mitte.
Puhh, so langsam verstehe ich und begreife mehr, ach ist das Toll :)
zerall
Ich verstehe nicht, warum eine FFT schneller sein soll, als ein simples
FIR. Beim FIR sind die Frequenzen N,N-1,N-2, ... , 1,0 ja schon
zusammengefasst. Und linearer ist es auch noch.
Hi,
Ich habe der FIR Sache nochmal eine Chance gegeben, es muss wohl damals
am Compiler gelegen haben (oder ähnlichen Problem). Denn ich habe soeben
einen 1000 Taps breiten FIR lowpass ziemlich schnell ausgeführt! In
10sec Samples der Länge von 5:00Min.
Damit kann ich mich zufrieden geben, dann kann ich somit die FFT aus dem
Spiel lassen. Hab die Sache eben schnell from scratch wie damals
programmiert, zuerst mit floats dann sogar doubles gerechnet, wobei kein
signifikanter Unterschied ensteht im Bezug auf die Berechnungsdauer.
Wobei 1000Taps schon übertrieben sind. Als Fenster diesmal ein Hann auf
den FIR.
Danke für die Motivation!, hat mich nun weitergebracht, auch was die
Erfahrung betrifft.
Warum es damals langsamer war, ca 1 Min Dauer für Berechnung, kann ich
nur auf den Compiler zurückführen. Oder irgendwas hat damals das System
verlangsamt. Die Hardware ist die selbe, gleiche cpu, speicher etc, nur
Visual Studio 2010 neu drauf.
Was ich jetzt mal versuche ist, rein aus Interesse, den FIR auf der GPU
(cuda) durchzuführen, also die Multiplikationen. Die Additionen muss
dann eben wieder von CPU klar.
Ich werde darüber berichten! Problem ist somit gelöst!
Danke und Gruß, zerall
Könntest du das posten? Z.B. in der Rubrik CODE-Schnipsel?
Mich interessiert nämlich wie das mit dem Lecken aussieht. Wenn Du
filterst, ensteht ja wie bei der FFT ein Leck an den Rändern der
Begrenzung. Oder ignorierst du das nun einfach?
Hi,
Klar kein Problem :), ich poste es mal auf nopaste da es nur ein Stück
Code ist, bzw noch im Test-Stadium.
Also wenn ich die Convolution mit dem FIR (Sinc+Fensterfunktion) direkt
berechne, also mit 2 Schleifen über das Signal, bekomme ich keine harten
Sprünge und Leakages mehr im Gegensatz zur FFT Methode, die ich zuvor
versucht hatte.
Die 2 Schleifen benötigen N mal M Rechenoperationen, wobei N die
Filter-Taps und M die Länge des Eingangsignals in Samples.
Ich habe das Ganze auch mal auf NVIDIA CUDA geported, also damit kann
man die Convolution auf dem Grafikprozessor (GPU) durchführen. Ist auf
jeden Fall ein enormer Geschwindigkeits Unterschied im Vergleich zur
CPU.
Falls jemand sich mit CUDA auseinandersetzt, poste ich den code auch
optional dazu. Dabei habe ich das SDK-Sample "vectorAdd" als Vorlage
benutzt und verändert.
Normale Version: http://nopaste.info/18c44cc0f5.html
CUDA Version: http://nopaste.info/1ac57f6349.html
Bei Fragen oder Hinweisen, Kritik einfach Antworten, Danke :)
Gruß, zerall
Du könntest das Erzeugen der Filterkoeffizienten noch Verbessern, in dem
Du eine gerade Zahl von Koeffizienten nimmst, vollständig
spiegelsymmetrisch baust, dabei zwei Filterkoeffizienten gleichzeitig
schreibst und das Window auch gleich draufmultiplizierst.
Technisch etwas verbessern könnte man es mit einem blackman-Window, das
für diese App (single-FFT) z.B. eine bessere Sperrdämpfung des Filters
generiert. Wenn Du nur einen Tiefpass willst (der headroom zur fs also
weiterhin gross ist) böte sich auch ein butterworth(5,7) als Filterform
an. Das liefert die bessere Audioqualität.
>Du könntest das Erzeugen der Filterkoeffizienten noch Verbessern, in dem>Du eine gerade Zahl von Koeffizienten nimmst, vollständig>spiegelsymmetrisch baust...
Stimmt so spart man sich den halben Prozess, Danke! :)
Hier gleich die verbesserte Version:
http://nopaste.info/2d1ef2174b.html
(Da ich mit den Fenstern noch rumspiele, berechne ich diese noch
nachträglich)
>Technisch etwas verbessern könnte man es mit einem blackman-Window - bessere >Sperrdämpfung
Stimmt, genial Thx! :)
(btw, bemerke gerade im Bezug auf Fenster Funktionen, dass die englische
wikipedia etwas mehr Informationen als die deutsche wiki zu bieten hat +
Spektral-Plots, cool)
>Wenn Du nur einen Tiefpass willst (der headroom zur fs also>weiterhin gross ist) böte sich auch ein butterworth(5,7) als Filterform>an. Das liefert die bessere Audioqualität.
Muss ich mir mal anschauen, bzw: gibt es da eine gute und einfache
Möglichkeit den Butterworth einfach zu generieren? Ich finde gerade nur
zu theoretisches Material im Netz.., da ich die z-Tranformation noch
lernen muss.
Zusatz: Habe soeben die convolution beschleunigen können indem ich
wieder die FFT verwende anstatt es direkt zu berechnen. Diesmal habe ich
aber den Kernel (den zuvor berechneten Sinc) in den Spektral-Bereich
(Amp + Phase) transformiert und anschließend zum Spektrum des
Eingangssignals dazugerechnet. Wobei Amplituden multipliziert und Phasen
addiert.
Nun enstehen keine leakages mehr wie zuvor beim einfachen "auf null
setzen" der Frequenzen. Es lag wohl an den Phasen des richtigen Sincs..
:)
Code-Ausschnitt:
http://nopaste.info/8b096bf65c.html
Gruß, zerall
Achtung: Habe einen Fehler in der Filter Funktion korrigiert, ein Wert
wurde als int zur sin Berechnung übergeben und hatte somit falsche Werte
erzeugt.
Hier die aktuellere und fehlerfreie Version:
http://nopaste.info/7db49b5948.html
Gruß, zerall
Jürgen Schuhmacher schrieb:> Wenn Du nur einen Tiefpass willst (der headroom zur fs also> weiterhin gross ist) böte sich auch ein butterworth
Da würde ich aber eher eine Bessel-Filter nehmen, ist effektiver