I tried all I could think of regarding the DC and Nyquist bins of the RAFX fft object, but I can’t seem to solve to problem: silence at 90 degrees phase shift. For now I will be using the HiFi-LoFi OouraFFT, but I would like to know how to use the RAFX fft object properly… I tried treating the DC and Nyquist bins separately but my results don’t change. Did you manage to get the RAFX fft object to work Tom? And if so, can you maybe share your solution with me?
Thanks in advance!
Tom, that was some great sleuthing - I was not aware that FFT libraries were doing that with DC/Nyquist, but I'd also like to know if you found the same thing in the RAFX FFT object (that object uses FFT code that has been around since the 1990's -- it is very tried-and-true so I am curious that these issues have not been reported before). Tom/JD, if either of you would like to publish an App Note on basic frequency domain RackAFX plugin programming, that would be AWESOME! You could also do a joint-publication as co-authors. I am getting more and more email from engineering students who are using RackAFX and FFT processing for senior design projects. So, let me know if you are interested. A robot-voice plugin example would also be killer.
Meantime, I have been super busy with RAFX v6.6. I think you will be blown away by what's in this version. I posted a sneak peak here:
All the best,
Wow, very impressive new features! Can’t wait to try it out
I would love to write an App Note on basic frequency domain RackAFX plug-in programming, but I have some studying to do first I’m afraid… I’m still mainly working single-threaded for my FFT tests, and did not yet get the RAFX FFT object to fully work (see earlier post). I do want to thank you for the opportunity and hope your offer still stands when I’m ready for it. That being said, I have a lot of writing experience (mostly Dutch but English as well) on the subject of music production, and if I can help Tom out somehow - if he decides to write an App Note - I’m happy to contribute in any way.
Have a nice weekend guys!
Hi Will, Hi JD
sounds great really I could well imagine writing an app note with JD. I guess the "difficult" thing would be to select a proper example which does not need too much "accessory parts" to make sense. JD, I guess together we should not have much problems to get some app notes done. I have a lot of writing experience too (...and I always wanted to learn Dutch *g* (I live in Aachen btw))
Maybe we could start off with another app note on concurrent FFT, e.g. I could do a C++11 version of the "parallel FFT" example which does not use Windows-specific things like SetEvent (e.g. calling SetEvent from the process method imho already is a violation of the VST specs) and should work unmodified on macs then, too! Then we could maybe do a follow up with the robo-voice example?
I guess when v6.6 is ready, I could do some more on CViews / OpenGL - especially OpenGL can get very confusing for those who don't know
Regarding the FFT thing: I'm still investigating a phenomenon - I did a "naive" phase rotation with the CFFT object (including the wrong-DC-imaginary) and it worked perfectly... Then I "cleaned it up", e.g. nulled the imaginary buffer before the FFT, not touch the DC-phase and all of a sudden it went back to the "90°-silence" behavior (which is "correct").
I realized that I have created a "dirty complex signal" consisting of the real part of the current buffer and the imaginary part of the last buffer's IFFT. Mathematically this is just plain garbage - but as a matter of fact it (audibly) works! I guess as the buffer size is small, the ffts are very similar so the "old" imaginary values seem to be "close enough", at least with my test signals. There are some artifacts at the borders, but those would go away with windowing... I also see a "cool" LFO-like drifting in the rotated signal when nyquist is rotated - this behaviour could be of great creative use *maybe*, so I try to "catch" it before eliminating it
But as a short preview to the (most probably) "solution" to the problem: The HiFiLoFi-FFT is a real-valued FFT, but the CFFT object in RackAFX does the "normal" complex DFT - this has an impact on how to interpret the results!
I currently have no RackAFX example ready with windowing - but after stepping back from "bughunting" and rethinking I realized that the approach HAS to produce artifacts when no windowing/overlap is applied, as of course the phase-shifted (i.e. delayed) partials have to "move" into the next buffer *facepalm* (which is what happened to some extend by accident with my "lazy" approach of passing dirty imaginary buffers into each next FFT iteration).
Did you take into account that the AudioFFT does a real-valued DTFT, which returns N/2 + 1 values which represent the frequency bins from DC up to nyquist. A complex DTFT will return N bins from DC up to Nyquist and then down the "negative" frequencies.
I'll drop you a pm, maybe you can send me your code an I'll have a look.
Hi Tom and JD
I just put up the first (new) FFT App Note, AN-11. It was written by one of my current students, Andrew Stockton, who is currently doing an internship at Bose, working in their DSP division. He will be a senior undergrad in the fall. He did this FFT/IFFT project just for fun - it was not part of a class assignment so I never graded it. He also implemented his own FFT/IFFT and in this project, multi-threading and buffering are not needed - he calls the FFT function on every sample interval (?!?!) When compiled in Debug mode, it only lights 3 LEDs on the CPU meter! The whole thing is done in processAudioFrame() and he implements 4 algorithms (high and low shelving filters, a crude but interesting pitch shifter, and a magnitude/phase swap algorithm. This is more of an example project than an App Note because the documentation is very short, but I have a RackAFX/book reader that requested more information. He also shows Hann windowing and 50% overlap/add so it is a nice project. When using the pitch shifter, start with the "Value" slider at 1.0 and then drop it from there.
I still very much want you guys to write your App Note(s) as well, with more thorough (deeper?) explanations like the rest of the App Notes. But, I think Andy's project is quite interesting. I have been tied up with both RackAFX-coding and restoring an old boat, so I do not have a lot of time to scrutinize this project or his FFT code -- maybe you guys could have a look at his implementation and let me know if you spot anything wrong. I tentatively named his link "Basic FFT Processing" and would like to add you work as "Intermediate" or "Advanced" titles.
Tom - JD is having problems replying to this thread but as far as I can tell, there is no limit to the number of replies on a Topic. He may start a second thread to continue this one, so be on the lookout for that. Also, let me know if you are also not able to add another reply here.
Let me know if you need anything on my end for the App Note stuff - and, no big hurry or rush there.
Hi Tom and Will,
My message is getting dated already, but here's another attempt without smiley’s: (I'll have a look at the new App Note soon!)
Cool, Aachen is just a few hours from here… Quite the coincidence. About the FFT App Note; I still feel I’m a little in over my head. After all, I just started C++ (and programming in general) and DSP a year and a half ago. I think, because of my experience in mastering, I can trust my ears and learn relatively fast, so stuff like FFT and Convolution might not be not very intimidating to me; but still (very) complex. So from a technical point of view I’m not sure if I’m of much use for now. I do have a ‘beginners’ perspective though, which might help with proofreading or checking out example code.
@Tom, I think your thoughts about the 90 degree silence problem are really interesting. I think I did take your comments into account when writing my test plug-in, and did not re-use the old imaginary array in the time domain. That’s probably why I do have consistent ‘wrong’ results – nulling the imaginary input array in the time domain. But I can’t really wrap my head around correctly interpreting the real valued results out of the complex valued FFT. I’m pretty sure that’s where the problem lays, like you said. But to be honest (@Will), I think the complex valued FFT making matters very complex in an audio context, especially for your target group. A dedicated real valued FFT would be much more intuitive to me, especially when there would be a seperate function for the FFT and IFFT. When I manage to understand the issue completely, I will try to write my own FFT class. If I get it running, I’m more than happy to share it here on the forum.
@Tom, I do have an example ready with windowing and 50% overlap-add (with the RackAFX FFT as well as the HiFi-LoFi FFT). Let me just run through it once more tomorrow. I’ll be happy to send it over to you!(I got your email!)
I do think this whole FFT thing is pretty intriguing and fun. So I’ll dive into it again the coming week!
I got it now
It's so simple when you know it
You basically have to account for the differences between complex and real DTFT: here is a nice explanation:
This is exactly what AudioFFT does (and it then calls the ooura real-dft, which only re-orders the array and then call the complex DTFT algorithm). The "real DTFT" actually is a pre-/post-processing of the FFT data, which exploits the symmetry property of the DTFT for real signals: The input imaginary array is only zeroes in the time domain, which in the frequency domain results in the real part mirrored at nyquist and the imaginary part mirrored and inverted (the math term is "complex conjugate").
And this is exactly the point that get's "dangerous" when you modify the values i.e. do Frequency-Space processing, you have to keep these properties intact, or you'll get a complex signal from the inverse DTFT. The "lazy" solution is to only calculate/modify the first half of freqeuncy bins and then mirror/invert those values to the bins above nyquist (i.e. the "negative" frequencies). This is what you do with the real DTFT. You of course can process those bins, too, but you have to get the same results (but conjugated) that you already did calulate for the first half of the bins...so it's a waste of CPU (for real signals).
I'll send you some brushed-up code in some days, with comments and hints - as you'll see, your rotation code can be expressed much more efficiently... but from a pure math aspect it is all correct besides the bins "above" nyquist have to be rotated into the other direction.
Have a nice weekend
Awesome I’m in the middle of leaving for Italy for two weeks, but I’m very happy with your message! I’ll be bringing my programming laptop anyway to do some ‘recreational’ coding, so I can work on it some more. I just copied my FFT tests to it and I’ll check the link soon.
I’m aware of the fact that the phase rotation code can be expressed more efficiently, but I left it like this so this project can function as a ‘frequency domain template’ for other tests as well. However, I’m very curious about your specific improvements, and want to thank you for brushing up and commenting on my project
Have a nice weekend!
This is where the content of this thread comes in- Will took a guess that it might be due to the Nyquist bin storing issue you mentioned. I looked closely at the FFT implementation I'm using and it appears that is indeed happening, as this line appears in the forward FFT routine:
// Pack nyquist component.
imag = real[bufferSize / 2];
However, I'm afraid I don't completely understand the implications of this or the solution. Could an IFFT with the Nyquist bin value in the imaginary section cause the problems I'm hearing? And I read through the link you posted, but the problem persists even after I zero negative frequencies. Is this likely to be caused by something else then?
On the other hand, the artifacts you describe also sound a bit like to the problems I had before with using the RAFX FFT object. During my tests correcting the zero and Nyquist bins did not really made a big difference; correcting for the real/complex FFT did! Tom send me this link, which was very helpful to me: http://www.dspguide.com/ch12/1.htm. It essentially explains how you have to approach doing a ‘real’ FFT, with a ‘complex’ FFT class. But it seems you have tried this already...
Hope it helps!
Thanks for the reply.
I may have misidentified the problem. If I just do overlap/add and do the FFT/IFFT with no spectral processing in the middle, everything sounds perfect, so I think I'm doing the real/complex FT stuff correctly- plus I read that link and I think I have a good grasp of it, and am pretty sure I'm doing everything it says. However, the artifacts are introduced even when I keep the pitch shift factor at 1.0 and don't change the pitch at all, so it's probably something I'm doing wrong with my actual pitch detection and shifting code...though I'm following the sample code pretty closely in that regard and have checked it many times, so I'm not sure what the problem would be.
I suppose it wouldn't hurt to take a look at your object if you could send me that. I definitely feel like I'm very close, but there's just something I'm missing with this.
Here’s a link to my pitch shift class: https://www.dropbox.com/s/e2w0uc5hxdjob3o/PitchClass.zip?dl=0
Btw, in the Zynaptiq code they also implement oversampling. If I have it set a 4 or higher the code sounds very nice. If I just leave it at 1, I get horrible fluttering. Did you also implement oversampling?
Just ran across this link and thought I'd tag it onto this thread:
Most Users Ever Online: 152
Currently Browsing this Page:
Guest Posters: 1
Newest Members:TheJonDoe, DoubleLiines.com, jmf11, dan, Luke Bilodeau, Carlos_1, ant, marclingk, TheCammen, Hubbert
Moderators: W Pirkle: 444
Administrators: Tom: 74, JD Young: 80, Will Pirkle: 0, W Pirkle: 444