SongData.cs 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. using SongVocalSectionAnalyser.Common.Collections;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. namespace SongVocalSectionAnalyser.AudioAnalysis
  6. {
  7. public class SongData
  8. {
  9. public IReadOnlyList<short> Samples { get; }
  10. public IReadOnlyList<SongDataChunk> Chunks { get; }
  11. public IReadOnlyList<float> MaLine { get; }
  12. public int SampleRate { get; }
  13. public int SamplePerChunk { get; }
  14. public int ChunksPerMa { get; }
  15. public SongData(IReadOnlyList<short> samples, int sampleRate, int msPerChunk, float chunkThreshold, int chunkDebounce, int chunksPerMa)
  16. {
  17. Samples = samples;
  18. SampleRate = sampleRate;
  19. ChunksPerMa = chunksPerMa;
  20. SamplePerChunk = (int)(sampleRate / 1000f * msPerChunk);
  21. var arrayChunkDb = samples
  22. .ChunkBy(SamplePerChunk)
  23. .Select((p, i) =>
  24. {
  25. return p.Select(s =>
  26. {
  27. var volume = Math.Abs(s / 32768.0);
  28. var decibels = 20 * Math.Log10(volume);
  29. return (float)decibels;
  30. }).Where(d => false == float.IsNegativeInfinity(d))
  31. .ToArray();
  32. }).ToArray();
  33. var minDb = arrayChunkDb.Where(p => p.Any()).Select(p => p.Min()).Min();
  34. var avgArr = arrayChunkDb
  35. .Select(p => p.Any() ? p : new[] { minDb })
  36. .Select(p => p.Average())
  37. .ToArray();
  38. var maxAvg = avgArr.Max();
  39. var minAvg = avgArr.Min();
  40. var dif = maxAvg - minAvg;
  41. var thr = minDb + dif * chunkThreshold / 100;
  42. var up = Math.Abs(minDb);
  43. Chunks = avgArr
  44. .Select((p, i) => new SongDataChunk(p + up, p >= thr, TimeSpan.FromMilliseconds(i * msPerChunk), TimeSpan.FromMilliseconds((1 + i) * msPerChunk - 1)))
  45. .ToArray();
  46. reGr:
  47. var list = Chunks.GroupContiguous(p => p.OverThreshold).ToList();
  48. foreach (var item in list.Skip(1).Where(p => p.Elements.Length < chunkDebounce).Take(list.Count - 2).ToArray())
  49. {
  50. var t = list[list.IndexOf(item) + 1].Key;
  51. foreach (var element in item.Elements)
  52. {
  53. element.OverThreshold = t;
  54. element.Debounced = true;
  55. }
  56. goto reGr;
  57. }
  58. MaLine = Chunks
  59. .Select((t, i) => Chunks.Skip(i).Take(chunksPerMa).ToArray())
  60. .TakeWhile(maSource => maSource.Length >= chunksPerMa)
  61. .Select(maSource => maSource.Select(c => c.Value).Average())
  62. .ToArray();
  63. }
  64. public SongData(SongData data, int msPerChunk, float chunkThreshold, int chunkDebounce, int chunksPerMa) : this(data.Samples, data.SampleRate, msPerChunk, chunkThreshold, chunkDebounce, chunksPerMa)
  65. {
  66. }
  67. }
  68. }