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.

292 lines
6.9KB

  1. #include "asserts.h"
  2. #include <memory>
  3. #include <set>
  4. #include "AudioMath.h"
  5. #include "FFTData.h"
  6. #include "FFT.h"
  7. extern void testFinalLeaks();
  8. static void testAccessors()
  9. {
  10. FFTDataReal d0(16);
  11. d0.set(0, 4);
  12. assertEQ(d0.get(0), 4);
  13. FFTDataCpx dc(16);
  14. cpx x(3, 4);
  15. dc.set(5, x);
  16. assertEQ(dc.get(5), x);
  17. }
  18. static void testFFTErrors()
  19. {
  20. FFTDataReal real(16);
  21. FFTDataCpx cpx(15);
  22. const bool b = FFT::forward(&cpx, real);
  23. assert(!b); // should error if size mismatch
  24. }
  25. static void testForwardFFT_DC()
  26. {
  27. FFTDataReal real(16);
  28. FFTDataCpx complex(16);
  29. // set real for DC
  30. for (int i = 0; i < 16; ++i) {
  31. real.set(i, 1.0);
  32. }
  33. const bool b = FFT::forward(&complex, real);
  34. assert(b);
  35. for (int i = 0; i < 16; ++i) {
  36. cpx v = complex.get(i);
  37. float mag = std::abs(v);
  38. float expect = (i == 0) ? 1.f : 0.f;
  39. assertEQ(mag, expect);
  40. }
  41. }
  42. static void test3()
  43. {
  44. FFTDataReal real(16);
  45. FFTDataCpx complex(16);
  46. // set real for fundamental sin.
  47. // make peak 2.0 so fft will come out to one
  48. for (int i = 0; i < 16; ++i) {
  49. auto x = 2.0 *sin(AudioMath::Pi * 2.0 * i / 16.0);
  50. real.set(i, float(x));
  51. }
  52. const bool b = FFT::forward(&complex, real);
  53. assert(b);
  54. for (int i = 0; i < 16; ++i) {
  55. cpx v = complex.get(i);
  56. float mag = std::abs(v);
  57. float expect = (i == 1) ? 1.f : 0.f;
  58. assertClose(mag, expect, .0001);
  59. }
  60. }
  61. static void testRoundTrip()
  62. {
  63. FFTDataReal realIn(16);
  64. FFTDataReal realOut(16);
  65. FFTDataCpx complex(16);
  66. // set real for DC
  67. for (int i = 0; i < 16; ++i) {
  68. realIn.set(i, 1.0);
  69. }
  70. bool b = FFT::forward(&complex, realIn);
  71. assert(b);
  72. b = FFT::inverse(&realOut, complex);
  73. for (int i = 0; i < 16; ++i) {
  74. float expect = 1.f; // scaled DC (TODO: fix scaling)
  75. assertEQ(realOut.get(i) , expect);
  76. }
  77. }
  78. static void testNoiseFormula()
  79. {
  80. const int bins = 64 * 1024 ;
  81. std::unique_ptr<FFTDataCpx> data(new FFTDataCpx(bins));
  82. assertEQ(data->size(), bins);
  83. FFT::makeNoiseSpectrum(data.get(), ColoredNoiseSpec());
  84. std::set<float> phases;
  85. for (int i = 0; i < bins; ++i) {
  86. const cpx x = data->get(i);
  87. float mag = std::abs(x);
  88. float phase = std::arg(x);
  89. const float expectedMag = (i == 0) ? 0.f : (i < (bins / 2)) ? 1.f : 0.f;
  90. assertClose(mag, expectedMag, .0001);
  91. phases.insert(phase);
  92. }
  93. }
  94. static float getPeak(const FFTDataReal& data)
  95. {
  96. float peak = 0;
  97. for (int i = 0; i < data.size(); ++i) {
  98. peak = std::max(peak, std::abs(data.get(i)));
  99. }
  100. return peak;
  101. }
  102. static void testWhiteNoiseRT()
  103. {
  104. const int bins = 2048;
  105. std::unique_ptr<FFTDataCpx> noiseSpectrum(new FFTDataCpx(bins));
  106. std::unique_ptr<FFTDataReal> noiseRealSignal(new FFTDataReal(bins));
  107. std::unique_ptr<FFTDataCpx> noiseSpectrum2(new FFTDataCpx(bins));
  108. for (int i = 0; i < bins; ++i) {
  109. cpx x(0,0);
  110. noiseSpectrum2->set(i, x);
  111. }
  112. FFT::makeNoiseSpectrum(noiseSpectrum.get(), ColoredNoiseSpec());
  113. FFT::inverse(noiseRealSignal.get(), *noiseSpectrum);
  114. FFT::forward(noiseSpectrum2.get(), *noiseRealSignal);
  115. float totalPhase = 0;
  116. float minPhase = 0;
  117. float maxPhase = 0;
  118. for (int i = 0; i < bins/2; ++i) {
  119. float expected = (i == 0) ? 0.f : 1.f;
  120. cpx data = noiseSpectrum2->get(i);
  121. assertClose(std::abs(data), expected, .0001);
  122. const float phase = std::arg(data);
  123. totalPhase += phase;
  124. minPhase = std::min(phase, minPhase);
  125. maxPhase = std::max(phase, maxPhase);
  126. //printf("phase[%d] = %f\n", i, std::arg(data));
  127. }
  128. //printf("TODO: assert on phase\n");
  129. //printf("total phase=%f, average=%f\n", totalPhase, totalPhase / (bins / 2));
  130. //printf("maxPhase %f min %f\n", maxPhase, minPhase);
  131. }
  132. static void testNoiseRTSub(int bins)
  133. {
  134. std::unique_ptr<FFTDataCpx> dataCpx(new FFTDataCpx(bins));
  135. std::unique_ptr<FFTDataReal> dataReal(new FFTDataReal(bins));
  136. assertEQ(dataCpx->size(), bins);
  137. FFT::makeNoiseSpectrum(dataCpx.get(), ColoredNoiseSpec());
  138. FFT::inverse(dataReal.get(), *dataCpx);
  139. FFT::normalize(dataReal.get());
  140. const float peak = getPeak(*dataReal);
  141. assertClose( peak, 1.0f , .001);
  142. }
  143. static void testNoiseRT()
  144. {
  145. testNoiseRTSub(4);
  146. testNoiseRTSub(8);
  147. testNoiseRTSub(16);
  148. testNoiseRTSub(1024);
  149. testNoiseRTSub(1024 * 64);
  150. }
  151. static void testPinkNoise()
  152. {
  153. const int bins = 1024*4;
  154. std::unique_ptr<FFTDataCpx> data(new FFTDataCpx(bins));
  155. assertEQ(data->size(), bins);
  156. ColoredNoiseSpec spec;
  157. spec.highFreqCorner = 22100; // makes no difference for - slope;
  158. spec.slope = -3;
  159. spec.sampleRate = 44100;
  160. FFT::makeNoiseSpectrum(data.get(), spec);
  161. // pick a starting bin above our 40 hz low freq corner
  162. const int baseBin = 16;
  163. //float freqBase = 44100 * baseBin / (float) bins;
  164. const float freqBase = FFT::bin2Freq(baseBin, 44100, bins);
  165. assertGT (freqBase, 80);
  166. // mid-band, quadruple freq should reduce amp by 6db
  167. float mag16 = std::abs(data->get(baseBin));
  168. float mag64 = std::abs(data->get(4 * baseBin));
  169. // TODO: compare in db
  170. assertClose(mag16, 2 * mag64, .01);
  171. float lastMag = std::abs(data->get(1));
  172. for (int i = 1; i < bins / 2; ++i) {
  173. const float mag = std::abs(data->get(i));
  174. assertLE(mag, lastMag);
  175. lastMag = mag;
  176. }
  177. for (int i = bins / 2; i < bins; ++i) {
  178. assertClose(std::abs(data->get(i)), 0, .00001);
  179. }
  180. }
  181. static void testBlueNoise(float corner = 0)
  182. {
  183. const int bins = 1024 * 4;
  184. std::unique_ptr<FFTDataCpx> data(new FFTDataCpx(bins));
  185. assertEQ(data->size(), bins);
  186. ColoredNoiseSpec spec;
  187. spec.slope = 3;
  188. spec.sampleRate = 44100;
  189. if (corner != 0) {
  190. spec.highFreqCorner = corner;
  191. } else {
  192. assertEQ(spec.highFreqCorner, 4000);
  193. }
  194. FFT::makeNoiseSpectrum(data.get(), spec);
  195. float freq16 = 44100 * 16 / (float) bins;
  196. assertGT(freq16, 20);
  197. // mid-band, quadruple freq should reduce amp by 6db
  198. float mag16 = std::abs(data->get(16));
  199. float mag64 = std::abs(data->get(64));
  200. assertClose(2 * mag16, mag64, .1);
  201. float lastMag = 0;
  202. for (int i = 1; i < bins / 2; ++i) {
  203. const float mag = std::abs(data->get(i));
  204. assertGE(mag, .999f * lastMag);
  205. lastMag = mag;
  206. }
  207. for (int i = bins / 2; i < bins; ++i) {
  208. assertClose(std::abs(data->get(i)), 0, .00001);
  209. }
  210. }
  211. void testFFT()
  212. {
  213. assertEQ(FFTDataReal::_count, 0);
  214. assertEQ(FFTDataCpx::_count, 0);
  215. testAccessors();
  216. testFFTErrors();
  217. testForwardFFT_DC();
  218. test3();
  219. testRoundTrip();
  220. testNoiseFormula();
  221. testNoiseRT();
  222. testPinkNoise();
  223. testBlueNoise();
  224. testBlueNoise(8000.f);
  225. testWhiteNoiseRT();
  226. testFinalLeaks();
  227. }