FFT.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace FFT
  5. {
  6. public class FourierTransform
  7. {
  8. public const int Raw = 1;
  9. public const int Decibel = 2;
  10. public const int FREQUENCYSLOTCOUNT = 21;
  11. private static int[] _meterFrequencies = new int[FREQUENCYSLOTCOUNT] { 20, 30, 55, 80, 120, 155, 195, 250, 375, 500, 750, 1000, 1500, 2000, 3000, 4000, 6000, 8000, 12000, 16000, 20000 };
  12. private static int _frequencySlotCount = FREQUENCYSLOTCOUNT;
  13. /// <summary>
  14. /// Changes the Frequency Bands to analyze.
  15. /// Affects the other static methods
  16. /// </summary>
  17. /// <param name="meterFrequencies"></param>
  18. public static void SetMeterFrequencies(int[] meterFrequencies)
  19. {
  20. _meterFrequencies = meterFrequencies;
  21. _frequencySlotCount = meterFrequencies.Length;
  22. }
  23. public static double[] Spectrum(ref double[] x, int method=Raw)
  24. {
  25. //uint pow2Samples = FFT.NextPowerOfTwo((uint)x.Length);
  26. double[] xre = new double[x.Length];
  27. double[] xim = new double[x.Length];
  28. Compute((uint)x.Length, x, null, xre, xim, false);
  29. double[] decibel = new double[xre.Length / 2];
  30. for (int i = 0; i < decibel.Length; i++)
  31. decibel[i] = (method == Decibel) ? 10.0 * Math.Log10((float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i])))) : (float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i])));
  32. return decibel;
  33. }
  34. public const Double DDC_PI = 3.14159265358979323846;
  35. /// <summary>
  36. /// Verifies a number is a power of two
  37. /// </summary>
  38. /// <param name="x">Number to check</param>
  39. /// <returns>true if number is a power two (i.e.:1,2,4,8,16,...)</returns>
  40. public static Boolean IsPowerOfTwo(UInt32 x)
  41. {
  42. return ((x != 0) && (x & (x - 1)) == 0);
  43. }
  44. /// <summary>
  45. /// Get Next power of number.
  46. /// </summary>
  47. /// <param name="x">Number to check</param>
  48. /// <returns>A power of two number</returns>
  49. public static UInt32 NextPowerOfTwo(UInt32 x)
  50. {
  51. x = x - 1;
  52. x = x | (x >> 1);
  53. x = x | (x >> 2);
  54. x = x | (x >> 4);
  55. x = x | (x >> 8);
  56. x = x | (x >> 16);
  57. return x + 1;
  58. }
  59. /// <summary>
  60. /// Get Number of bits needed for a power of two
  61. /// </summary>
  62. /// <param name="PowerOfTwo">Power of two number</param>
  63. /// <returns>Number of bits</returns>
  64. public static UInt32 NumberOfBitsNeeded(UInt32 PowerOfTwo)
  65. {
  66. if (PowerOfTwo > 0)
  67. {
  68. for (UInt32 i = 0, mask = 1; ; i++, mask <<= 1)
  69. {
  70. if ((PowerOfTwo & mask) != 0)
  71. return i;
  72. }
  73. }
  74. return 0; // error
  75. }
  76. /// <summary>
  77. /// Reverse bits
  78. /// </summary>
  79. /// <param name="index">Bits</param>
  80. /// <param name="NumBits">Number of bits to reverse</param>
  81. /// <returns>Reverse Bits</returns>
  82. public static UInt32 ReverseBits(UInt32 index, UInt32 NumBits)
  83. {
  84. UInt32 i, rev;
  85. for (i = rev = 0; i < NumBits; i++)
  86. {
  87. rev = (rev << 1) | (index & 1);
  88. index >>= 1;
  89. }
  90. return rev;
  91. }
  92. /// <summary>
  93. /// Return index to frequency based on number of samples
  94. /// </summary>
  95. /// <param name="Index">sample index</param>
  96. /// <param name="NumSamples">number of samples</param>
  97. /// <returns>Frequency index range</returns>
  98. public static Double IndexToFrequency(UInt32 Index, UInt32 NumSamples)
  99. {
  100. if (Index >= NumSamples)
  101. return 0.0;
  102. else if (Index <= NumSamples / 2)
  103. return (double)Index / (double)NumSamples;
  104. return -(double)(NumSamples - Index) / (double)NumSamples;
  105. }
  106. /// <summary>
  107. /// Compute FFT
  108. /// </summary>
  109. /// <param name="NumSamples">NumSamples Number of samples (must be power two)</param>
  110. /// <param name="pRealIn">Real samples</param>
  111. /// <param name="pImagIn">Imaginary (optional, may be null)</param>
  112. /// <param name="pRealOut">Real coefficient output</param>
  113. /// <param name="pImagOut">Imaginary coefficient output</param>
  114. /// <param name="bInverseTransform">bInverseTransform when true, compute Inverse FFT</param>
  115. public static void Compute(UInt32 NumSamples, Double[] pRealIn, Double[] pImagIn,
  116. Double[] pRealOut, Double[] pImagOut, Boolean bInverseTransform)
  117. {
  118. UInt32 NumBits; /* Number of bits needed to store indices */
  119. UInt32 i, j, k, n;
  120. UInt32 BlockSize, BlockEnd;
  121. double angle_numerator = 2.0 * DDC_PI;
  122. double tr, ti; /* temp real, temp imaginary */
  123. if (pRealIn == null || pRealOut == null || pImagOut == null)
  124. {
  125. // error
  126. throw new ArgumentNullException("Null argument");
  127. }
  128. if (!IsPowerOfTwo(NumSamples))
  129. {
  130. // error
  131. throw new ArgumentException("Number of samples must be power of 2");
  132. }
  133. if (pRealIn.Length < NumSamples || (pImagIn != null && pImagIn.Length < NumSamples) ||
  134. pRealOut.Length < NumSamples || pImagOut.Length < NumSamples)
  135. {
  136. // error
  137. throw new ArgumentException("Invalid Array argument detected");
  138. }
  139. if (bInverseTransform)
  140. angle_numerator = -angle_numerator;
  141. NumBits = NumberOfBitsNeeded(NumSamples);
  142. /*
  143. ** Do simultaneous data copy and bit-reversal ordering into outputs...
  144. */
  145. for (i = 0; i < NumSamples; i++)
  146. {
  147. j = ReverseBits(i, NumBits);
  148. pRealOut[j] = pRealIn[i];
  149. pImagOut[j] = (double)((pImagIn == null) ? 0.0 : pImagIn[i]);
  150. }
  151. /*
  152. ** Do the FFT itself...
  153. */
  154. BlockEnd = 1;
  155. for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1)
  156. {
  157. double delta_angle = angle_numerator / (double)BlockSize;
  158. double sm2 = Math.Sin(-2 * delta_angle);
  159. double sm1 = Math.Sin(-delta_angle);
  160. double cm2 = Math.Cos(-2 * delta_angle);
  161. double cm1 = Math.Cos(-delta_angle);
  162. double w = 2 * cm1;
  163. double ar0, ar1, ar2;
  164. double ai0, ai1, ai2;
  165. for (i = 0; i < NumSamples; i += BlockSize)
  166. {
  167. ar2 = cm2;
  168. ar1 = cm1;
  169. ai2 = sm2;
  170. ai1 = sm1;
  171. for (j = i, n = 0; n < BlockEnd; j++, n++)
  172. {
  173. ar0 = w * ar1 - ar2;
  174. ar2 = ar1;
  175. ar1 = ar0;
  176. ai0 = w * ai1 - ai2;
  177. ai2 = ai1;
  178. ai1 = ai0;
  179. k = j + BlockEnd;
  180. tr = ar0 * pRealOut[k] - ai0 * pImagOut[k];
  181. ti = ar0 * pImagOut[k] + ai0 * pRealOut[k];
  182. pRealOut[k] = (pRealOut[j] - tr);
  183. pImagOut[k] = (pImagOut[j] - ti);
  184. pRealOut[j] += (tr);
  185. pImagOut[j] += (ti);
  186. }
  187. }
  188. BlockEnd = BlockSize;
  189. }
  190. /*
  191. ** Need to normalize if inverse transform...
  192. */
  193. if (bInverseTransform)
  194. {
  195. double denom = (double)(NumSamples);
  196. for (i = 0; i < NumSamples; i++)
  197. {
  198. pRealOut[i] /= denom;
  199. pImagOut[i] /= denom;
  200. }
  201. }
  202. }
  203. /// <summary>
  204. /// Calculate normal (power spectrum)
  205. /// </summary>
  206. /// <param name="NumSamples">Number of sample</param>
  207. /// <param name="pReal">Real coefficient buffer</param>
  208. /// <param name="pImag">Imaginary coefficient buffer</param>
  209. /// <param name="pAmpl">Working buffer to hold amplitude Xps(m) = | X(m)^2 | = Xreal(m)^2 + Ximag(m)^2</param>
  210. public static void Norm(UInt32 NumSamples, Double[] pReal, Double[] pImag, Double[] pAmpl)
  211. {
  212. if (pReal == null || pImag == null || pAmpl == null)
  213. {
  214. // error
  215. throw new ArgumentNullException("pReal,pImag,pAmpl");
  216. }
  217. if (pReal.Length < NumSamples || pImag.Length < NumSamples || pAmpl.Length < NumSamples)
  218. {
  219. // error
  220. throw new ArgumentException("Invalid Array argument detected");
  221. }
  222. // Calculate amplitude values in the buffer provided
  223. for (UInt32 i = 0; i < NumSamples; i++)
  224. {
  225. pAmpl[i] = pReal[i] * pReal[i] + pImag[i] * pImag[i];
  226. }
  227. }
  228. /// <summary>
  229. /// Find Peak frequency in Hz
  230. /// </summary>
  231. /// <param name="NumSamples">Number of samples</param>
  232. /// <param name="pAmpl">Current amplitude</param>
  233. /// <param name="samplingRate">Sampling rate in samples/second (Hz)</param>
  234. /// <param name="index">Frequency index</param>
  235. /// <returns>Peak frequency in Hz</returns>
  236. public static Double PeakFrequency(UInt32 NumSamples, Double[] pAmpl, Double samplingRate, ref UInt32 index)
  237. {
  238. UInt32 N = NumSamples >> 1; // number of positive frequencies. (numSamples/2)
  239. if (pAmpl == null)
  240. {
  241. // error
  242. throw new ArgumentNullException("pAmpl");
  243. }
  244. if (pAmpl.Length < NumSamples)
  245. {
  246. // error
  247. throw new ArgumentException("Invalid Array argument detected");
  248. }
  249. double maxAmpl = -1.0;
  250. double peakFreq = -1.0;
  251. index = 0;
  252. for (UInt32 i = 0; i < N; i++)
  253. {
  254. if (pAmpl[i] > maxAmpl)
  255. {
  256. maxAmpl = (double)pAmpl[i];
  257. index = i;
  258. peakFreq = (double)(i);
  259. }
  260. }
  261. return samplingRate * peakFreq / (double)(NumSamples);
  262. }
  263. public static byte[] GetPeaks(double[] leftChannel, double[] rightChannel, int sampleFrequency)
  264. {
  265. byte[] peaks = new byte[_frequencySlotCount];
  266. byte[] channelPeaks = GetPeaksForChannel(leftChannel, sampleFrequency);
  267. ComparePeaks(peaks, channelPeaks);
  268. return peaks;
  269. }
  270. private static void ComparePeaks(byte[] overallPeaks, byte[] channelPeaks)
  271. {
  272. for (int i = 0; i < _frequencySlotCount; i++)
  273. {
  274. overallPeaks[i] = Math.Max(overallPeaks[i], channelPeaks[i]);
  275. }
  276. }
  277. private static byte[] GetPeaksForChannel(double[] normalizedArray, int sampleFrequency)
  278. {
  279. double maxAmpl = (32767.0 * 32767.0);
  280. byte[] peaks = new byte[_frequencySlotCount];
  281. // update meter
  282. int centerFreq = (sampleFrequency / 2);
  283. byte peak;
  284. for (int i = 0; i < _frequencySlotCount; ++i)
  285. {
  286. if (_meterFrequencies[i] > centerFreq)
  287. {
  288. peak = 0;
  289. }
  290. else
  291. {
  292. int index = (int)(_meterFrequencies[i] * normalizedArray.Length / sampleFrequency);
  293. peak = (byte)Math.Max(0, (17.0 * Math.Log10(normalizedArray[index] / maxAmpl)));
  294. }
  295. peaks[i] = peak;
  296. }
  297. return peaks;
  298. }
  299. }
  300. }