SpectrumAnalyzer.cs 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using BeatLyrics.Tool.RefLibs;
  2. using CSCore;
  3. using CSCore.Codecs.OGG;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Numerics;
  8. using System.Threading.Tasks;
  9. namespace BeatLyrics.Tool.Utils
  10. {
  11. internal static class SpectrumAnalyzer
  12. {
  13. public static SaResult[] RunOgg(string filePath, int blockSizeInMs, DSP.Window.Type windowType = DSP.Window.Type.Rectangular)
  14. {
  15. //extract samples from file
  16. using var ms = new MemoryStream(File.ReadAllBytes(filePath));
  17. using var ogg = new OggSource(ms);
  18. using var sampleSource = ogg.ToMono();
  19. var samples = new List<double[]>();
  20. var buffer = new float[(int)(sampleSource.WaveFormat.SampleRate * (blockSizeInMs / 1000f))];
  21. //Split by block size
  22. do
  23. {
  24. var read = sampleSource.Read(buffer, 0, buffer.Length);
  25. if (read == 0) break; // end of stream
  26. if (read < buffer.Length)
  27. {
  28. samples.Add(buffer.Take(read).Select(p => (double)p).ToArray());
  29. break;
  30. }
  31. samples.Add(buffer.Take(read).Select(p => (double)p).ToArray());
  32. } while (true);
  33. // calc each fft
  34. var results = new SaResult[samples.Count];
  35. Parallel.For(0, samples.Count, (i) =>
  36. {
  37. //feed samples to fft
  38. var timeSeries = samples[i];
  39. var N = (uint)timeSeries.Length;
  40. double[] wc = DSP.Window.Coefficients(windowType, N);
  41. double windowScaleFactor = DSP.Window.ScaleFactor.Signal(wc);
  42. double[] windowedTimeSeries = DSP.Math.Multiply(timeSeries, wc);
  43. FFT fft = new FFT();
  44. var pad = 0u;
  45. for (int pow = 1; pow <= 32; pow++)
  46. {
  47. var p = (uint)System.Math.Pow(2, pow);
  48. if (p >= N)
  49. {
  50. pad = p - N;
  51. break;
  52. }
  53. }
  54. fft.Initialize(N, pad);
  55. Complex[] cpxResult = fft.Execute(windowedTimeSeries);
  56. double[] mag = DSP.ConvertComplex.ToMagnitude(cpxResult);
  57. mag = DSP.Math.Multiply(mag, windowScaleFactor);
  58. double[] magLog = DSP.ConvertMagnitude.ToMagnitudeDBV(mag);
  59. double[] fSpan = fft.FrequencySpan(sampleSource.WaveFormat.SampleRate);
  60. var item = new SaResult { FreqAxis = fSpan, Values = magLog };
  61. results[i] = item;
  62. });
  63. return results;
  64. }
  65. public class SaResult
  66. {
  67. public double[] FreqAxis { get; set; }
  68. public double[] Values { get; set; }
  69. }
  70. }
  71. }