Forum: Digitale Signalverarbeitung / DSP / Machine Learning Low-Pass mit FFT (Problem)


von zerall (Gast)


Angehängte Dateien:

Lesenswert?

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

von Helmut S. (helmuts)


Lesenswert?

> 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?

von J. S. (engineer) Benutzerseite


Lesenswert?

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.

von zerall (Gast)


Lesenswert?

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

von Helmut S. (helmuts)


Lesenswert?

> 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"?

von zerall (Gast)


Lesenswert?

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

von Georg A. (Gast)


Lesenswert?

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.

von zerall (Gast)


Lesenswert?

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

von Martin K. (mkmannheim) Benutzerseite


Lesenswert?

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?

von zerall (Gast)


Lesenswert?

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

von J. S. (engineer) Benutzerseite


Lesenswert?

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.

von zerall (Gast)


Lesenswert?

>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

von zerall (Gast)


Lesenswert?

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

von Georg A. (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.