jurihock / stftPitchShift

STFT based real-time pitch and timbre shifting in C++ and Python
MIT License
115 stars 14 forks source link

Add optional FFTW3 support #6

Closed jurihock closed 2 years ago

jurihock commented 2 years ago

Although pocketfft is a handy and useful lib for development, the FFTW3 seems to be more suitable for distribution, since it's already available in Debian, VCPKG and Homebrew:

So add FFT abstraction layer in C++ and CMake option -DFFTW=ON to enable it.

jjYBdx4IL commented 2 years ago

though it has a very restrictive license, as do most of the other audio processing libs in vcpkg.

jurihock commented 2 years ago

oh ok, that's true, it has a dual license GPL or purchased MIT...

jurihock commented 2 years ago

Regardless of the license, the FFTW3 extension looks interesting to me, due to availability, probably speedup against the pocketfft and optional MIT license (who really need it, should buy it).

As a replacement for the pocketfft I can try to implement a simple recursive radix-2 fft with precomputed twiddle factors.

Probably it will be still fast enough on modern CPUs, even for real time audio at 4X kHz (which is beyond the scope of this project anyway).

jurihock commented 2 years ago

To eliminate external dependencies in the lib, I'll introduce an abstract interface for a FFT instance. Something like this:

class FFT
{
public:
  virtual ~FFT() {}
  virtual void fft(const std::vector<float>& frame, std::vector<std::complex<float>>& dft) = 0;
  virtual void ifft(const std::vector<std::complex<float>>& dft, std::vector<float>& frame) = 0;
};

Then the concrete FFT instance is to be provided via std::shared_ptr<FFT>. The frame size is known in advance, if required for FFT plan calculation...

In the second step, the pocketfft will be replaced by a default "public domain" implementation, e.g. if no user defined FFT instance is specified.

pocketfft backend example

#include <StftPitchShift/FFT.h>
#include <pocketfft/pocketfft_hdronly.h>

class Pocketfft : public FFT
{

public:

  void fft(const std::vector<float>& frame, std::vector<std::complex<float>>& dft) override
  {
    pocketfft::r2c(
      { frame.size() },
      { sizeof(float) },
      { sizeof(std::complex<float>) },
      0,
      true,
      frame.data(),
      dft.data(),
      1.0f / frame.size());
  }

  void ifft(const std::vector<std::complex<float>>& dft, std::vector<float>& frame) override
  {
    pocketfft::c2r(
      { frame.size() },
      { sizeof(std::complex<float>) },
      { sizeof(float) },
      0,
      false,
      dft.data(),
      frame.data(),
      1.0f);
  }

};
jurihock commented 2 years ago

After playing around with FFTW, I'm not happy about the results:

So I'll stop at this point.

CMakeLists.txt FFTW.h.txt