#include "AudioMath.h" #include "FFT.h" #include "FFTData.h" #include #include "kiss_fft.h" #include "kiss_fftr.h" #include "AudioMath.h" bool FFT::forward(FFTDataCpx* out, const FFTDataReal& in) { if (out->buffer.size() != in.buffer.size()) { return false; } // step 1: create the cfg, if needed if (in.kiss_cfg == 0) { bool inverse_fft = false; kiss_fftr_cfg newCfg= kiss_fftr_alloc((int)in.buffer.size(), inverse_fft, nullptr, nullptr); assert (newCfg); if (!newCfg) { return false; } // now save off in our typeless pointer. assert(sizeof(newCfg) == sizeof(in.kiss_cfg)); in.kiss_cfg = newCfg; } // step 2: do the fft kiss_fftr_cfg theCfg = reinterpret_cast(in.kiss_cfg); // TODO: need a test that this assumption is correct (that we kiss_fft_cpx == std::complex. kiss_fft_cpx * outBuffer = reinterpret_cast(out->buffer.data()); kiss_fftr(theCfg, in.buffer.data(), outBuffer); // step 4: scale const float scale = float(1.0 / in.buffer.size()); for (size_t i = 0; i < in.buffer.size(); ++i) { out->buffer[i] *= scale; } return true; } bool FFT::inverse(FFTDataReal* out, const FFTDataCpx& in) { if (out->buffer.size() != in.buffer.size()) { return false; } // step 1: create the cfg, if needed if (in.kiss_cfg == 0) { bool inverse_fft = true; kiss_fftr_cfg newCfg = kiss_fftr_alloc((int) in.buffer.size(), inverse_fft, nullptr, nullptr); assert(newCfg); if (!newCfg) { return false; } // now save off in our typeless pointer. assert(sizeof(newCfg) == sizeof(in.kiss_cfg)); in.kiss_cfg = newCfg; } // step 2: do the fft kiss_fftr_cfg theCfg = reinterpret_cast(in.kiss_cfg); // TODO: need a test that this assumption is correct (that we kiss_fft_cpx == std::complex. const kiss_fft_cpx * inBuffer = reinterpret_cast(in.buffer.data()); kiss_fftri(theCfg, inBuffer, out->buffer.data()); return true; } int FFT::freqToBin(float freq, float sampleRate, int numBins) { assert(freq <= (sampleRate / 2)); // bin(numBins) <> sr / 2; return (int)((freq / sampleRate)*(numBins * 2)); } float FFT::bin2Freq(int bin, float sampleRate, int numBins) { return sampleRate * float(bin) / (float(numBins) * 2); } static float randomPhase() { float phase = (float) rand(); phase = phase / (float) RAND_MAX; // 0..1 phase = (float) (phase * (2 * AudioMath::Pi)); return phase; } static void makeNegSlope(FFTDataCpx* output, const ColoredNoiseSpec& spec) { const int numBins = int(output->size()); const float lowFreqCorner = 40; // find bin for 40 Hz const int bin40 = FFT::freqToBin(lowFreqCorner, spec.sampleRate, numBins); // fill bottom bins with 1.0 mag for (int i = 0; i <= bin40; ++i) { output->set(i, std::polar(1.f, randomPhase())); } // now go to the end and at slope static float k = -spec.slope * log2(lowFreqCorner); for (int i = bin40 + 1; i < numBins; ++i) { if (i < numBins / 2) { const float f = FFT::bin2Freq(i, spec.sampleRate, numBins); const float gainDb = std::log2(f) * spec.slope + k; const float gain = float(AudioMath::gainFromDb(gainDb)); output->set(i, std::polar(gain, randomPhase())); } else { output->set(i, cpx(0, 0)); } } output->set(0, 0); // make sure dc bin zero } static void makePosSlope(FFTDataCpx* output, const ColoredNoiseSpec& spec) { const int numBins = int(output->size()); // find bin for high corner const int binHigh = FFT::freqToBin(spec.highFreqCorner, spec.sampleRate, numBins); // now go to the end and at slope float gainMax = 1; // even if nothing in the bins (ut) needs something in there. static float k = -spec.slope * log2(spec.highFreqCorner); for (int i = binHigh - 1; i > 0; --i) { if (i < numBins / 2) { const float f = FFT::bin2Freq(i, spec.sampleRate, numBins); const float gainDb = std::log2(f) * spec.slope + k; const float gain = float(AudioMath::gainFromDb(gainDb)); gainMax = std::max(gain, gainMax); output->set(i, std::polar(gain, randomPhase())); } else { output->set(i, cpx(0, 0)); } } // fill top bins with mag mag for (int i = numBins - 1; i >= binHigh; --i) { if (i < numBins / 2) { output->set(i, std::polar(gainMax, randomPhase())); } else { output->set(i, cpx(0.0)); } } output->set(0, 0); // make sure dc bin zero } void FFT::makeNoiseSpectrum(FFTDataCpx* output, const ColoredNoiseSpec& spec) { // for now, zero all first. const int frameSize = (int) output->size(); for (int i = 0; i < frameSize; ++i) { cpx x(0,0); output->set(i, x); } if (spec.slope < 0) { makeNegSlope(output, spec); } else { makePosSlope(output, spec); } } static float getPeak(const FFTDataReal& data) { float peak = 0; for (int i = 0; i < data.size(); ++i) { peak = std::max(peak, std::abs(data.get(i))); } return peak; } void FFT::normalize(FFTDataReal* data) { const float peak = getPeak(*data); const float correction = 1.0f / peak; for (int i = 0; i < data->size(); ++i) { float x = data->get(i); x *= correction; data->set(i, x); } }