You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
5.6KB

  1. #include "AudioMath.h"
  2. #include "FFT.h"
  3. #include "FFTData.h"
  4. #include <assert.h>
  5. #include "kiss_fft.h"
  6. #include "kiss_fftr.h"
  7. #include "AudioMath.h"
  8. bool FFT::forward(FFTDataCpx* out, const FFTDataReal& in)
  9. {
  10. if (out->buffer.size() != in.buffer.size()) {
  11. return false;
  12. }
  13. // step 1: create the cfg, if needed
  14. if (in.kiss_cfg == 0) {
  15. bool inverse_fft = false;
  16. kiss_fftr_cfg newCfg= kiss_fftr_alloc((int)in.buffer.size(),
  17. inverse_fft,
  18. nullptr, nullptr);
  19. assert (newCfg);
  20. if (!newCfg) {
  21. return false;
  22. }
  23. // now save off in our typeless pointer.
  24. assert(sizeof(newCfg) == sizeof(in.kiss_cfg));
  25. in.kiss_cfg = newCfg;
  26. }
  27. // step 2: do the fft
  28. kiss_fftr_cfg theCfg = reinterpret_cast<kiss_fftr_cfg>(in.kiss_cfg);
  29. // TODO: need a test that this assumption is correct (that we kiss_fft_cpx == std::complex.
  30. kiss_fft_cpx * outBuffer = reinterpret_cast<kiss_fft_cpx *>(out->buffer.data());
  31. kiss_fftr(theCfg, in.buffer.data(), outBuffer);
  32. // step 4: scale
  33. const float scale = float(1.0 / in.buffer.size());
  34. for (size_t i = 0; i < in.buffer.size(); ++i) {
  35. out->buffer[i] *= scale;
  36. }
  37. return true;
  38. }
  39. bool FFT::inverse(FFTDataReal* out, const FFTDataCpx& in)
  40. {
  41. if (out->buffer.size() != in.buffer.size()) {
  42. return false;
  43. }
  44. // step 1: create the cfg, if needed
  45. if (in.kiss_cfg == 0) {
  46. bool inverse_fft = true;
  47. kiss_fftr_cfg newCfg = kiss_fftr_alloc((int) in.buffer.size(),
  48. inverse_fft,
  49. nullptr, nullptr);
  50. assert(newCfg);
  51. if (!newCfg) {
  52. return false;
  53. }
  54. // now save off in our typeless pointer.
  55. assert(sizeof(newCfg) == sizeof(in.kiss_cfg));
  56. in.kiss_cfg = newCfg;
  57. }
  58. // step 2: do the fft
  59. kiss_fftr_cfg theCfg = reinterpret_cast<kiss_fftr_cfg>(in.kiss_cfg);
  60. // TODO: need a test that this assumption is correct (that we kiss_fft_cpx == std::complex.
  61. const kiss_fft_cpx * inBuffer = reinterpret_cast<const kiss_fft_cpx *>(in.buffer.data());
  62. kiss_fftri(theCfg, inBuffer, out->buffer.data());
  63. return true;
  64. }
  65. int FFT::freqToBin(float freq, float sampleRate, int numBins)
  66. {
  67. assert(freq <= (sampleRate / 2));
  68. // bin(numBins) <> sr / 2;
  69. return (int)((freq / sampleRate)*(numBins * 2));
  70. }
  71. float FFT::bin2Freq(int bin, float sampleRate, int numBins)
  72. {
  73. return sampleRate * float(bin) / (float(numBins) * 2);
  74. }
  75. static float randomPhase()
  76. {
  77. float phase = (float) rand();
  78. phase = phase / (float) RAND_MAX; // 0..1
  79. phase = (float) (phase * (2 * AudioMath::Pi));
  80. return phase;
  81. }
  82. static void makeNegSlope(FFTDataCpx* output, const ColoredNoiseSpec& spec)
  83. {
  84. const int numBins = int(output->size());
  85. const float lowFreqCorner = 40;
  86. // find bin for 40 Hz
  87. const int bin40 = FFT::freqToBin(lowFreqCorner, spec.sampleRate, numBins);
  88. // fill bottom bins with 1.0 mag
  89. for (int i = 0; i <= bin40; ++i) {
  90. output->set(i, std::polar(1.f, randomPhase()));
  91. }
  92. // now go to the end and at slope
  93. static float k = -spec.slope * log2(lowFreqCorner);
  94. for (int i = bin40 + 1; i < numBins; ++i) {
  95. if (i < numBins / 2) {
  96. const float f = FFT::bin2Freq(i, spec.sampleRate, numBins);
  97. const float gainDb = std::log2(f) * spec.slope + k;
  98. const float gain = float(AudioMath::gainFromDb(gainDb));
  99. output->set(i, std::polar(gain, randomPhase()));
  100. } else {
  101. output->set(i, cpx(0, 0));
  102. }
  103. }
  104. output->set(0, 0); // make sure dc bin zero
  105. }
  106. static void makePosSlope(FFTDataCpx* output, const ColoredNoiseSpec& spec)
  107. {
  108. const int numBins = int(output->size());
  109. // find bin for high corner
  110. const int binHigh = FFT::freqToBin(spec.highFreqCorner, spec.sampleRate, numBins);
  111. // now go to the end and at slope
  112. float gainMax = 1; // even if nothing in the bins (ut) needs something in there.
  113. static float k = -spec.slope * log2(spec.highFreqCorner);
  114. for (int i = binHigh - 1; i > 0; --i) {
  115. if (i < numBins / 2) {
  116. const float f = FFT::bin2Freq(i, spec.sampleRate, numBins);
  117. const float gainDb = std::log2(f) * spec.slope + k;
  118. const float gain = float(AudioMath::gainFromDb(gainDb));
  119. gainMax = std::max(gain, gainMax);
  120. output->set(i, std::polar(gain, randomPhase()));
  121. } else {
  122. output->set(i, cpx(0, 0));
  123. }
  124. }
  125. // fill top bins with mag mag
  126. for (int i = numBins - 1; i >= binHigh; --i) {
  127. if (i < numBins / 2) {
  128. output->set(i, std::polar(gainMax, randomPhase()));
  129. } else {
  130. output->set(i, cpx(0.0));
  131. }
  132. }
  133. output->set(0, 0); // make sure dc bin zero
  134. }
  135. void FFT::makeNoiseSpectrum(FFTDataCpx* output, const ColoredNoiseSpec& spec)
  136. {
  137. // for now, zero all first.
  138. const int frameSize = (int) output->size();
  139. for (int i = 0; i < frameSize; ++i) {
  140. cpx x(0,0);
  141. output->set(i, x);
  142. }
  143. if (spec.slope < 0) {
  144. makeNegSlope(output, spec);
  145. } else {
  146. makePosSlope(output, spec);
  147. }
  148. }
  149. static float getPeak(const FFTDataReal& data)
  150. {
  151. float peak = 0;
  152. for (int i = 0; i < data.size(); ++i) {
  153. peak = std::max(peak, std::abs(data.get(i)));
  154. }
  155. return peak;
  156. }
  157. void FFT::normalize(FFTDataReal* data)
  158. {
  159. const float peak = getPeak(*data);
  160. const float correction = 1.0f / peak;
  161. for (int i = 0; i < data->size(); ++i) {
  162. float x = data->get(i);
  163. x *= correction;
  164. data->set(i, x);
  165. }
  166. }