12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- using SongVocalSectionAnalyser.Common.Collections;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace SongVocalSectionAnalyser.AudioAnalysis
- {
- public class SongData
- {
- public IReadOnlyList<short> Samples { get; }
- public IReadOnlyList<SongDataChunk> Chunks { get; }
- public IReadOnlyList<float> MaLine { get; }
- public int SampleRate { get; }
- public int SamplePerChunk { get; }
- public int ChunksPerMa { get; }
- public SongData(IReadOnlyList<short> samples, int sampleRate, int msPerChunk, float chunkThreshold, int chunkDebounce, int chunksPerMa)
- {
- Samples = samples;
- SampleRate = sampleRate;
- ChunksPerMa = chunksPerMa;
- SamplePerChunk = (int)(sampleRate / 1000f * msPerChunk);
- var arrayChunkDb = samples
- .ChunkBy(SamplePerChunk)
- .Select((p, i) =>
- {
- return p.Select(s =>
- {
- var volume = Math.Abs(s / 32768.0);
- var decibels = 20 * Math.Log10(volume);
- return (float)decibels;
- }).Where(d => false == float.IsNegativeInfinity(d))
- .ToArray();
- }).ToArray();
- var minDb = arrayChunkDb.Where(p => p.Any()).Select(p => p.Min()).Min();
- var avgArr = arrayChunkDb
- .Select(p => p.Any() ? p : new[] { minDb })
- .Select(p => p.Average())
- .ToArray();
- var maxAvg = avgArr.Max();
- var minAvg = avgArr.Min();
- var dif = maxAvg - minAvg;
- var thr = minDb + dif * chunkThreshold / 100;
- var up = Math.Abs(minDb);
- Chunks = avgArr
- .Select((p, i) => new SongDataChunk(p + up, p >= thr, TimeSpan.FromMilliseconds(i * msPerChunk), TimeSpan.FromMilliseconds((1 + i) * msPerChunk - 1)))
- .ToArray();
- reGr:
- var list = Chunks.GroupContiguous(p => p.OverThreshold).ToList();
- foreach (var item in list.Skip(1).Where(p => p.Elements.Length < chunkDebounce).Take(list.Count - 2).ToArray())
- {
- var t = list[list.IndexOf(item) + 1].Key;
- foreach (var element in item.Elements)
- {
- element.OverThreshold = t;
- element.Debounced = true;
- }
- goto reGr;
- }
- MaLine = Chunks
- .Select((t, i) => Chunks.Skip(i).Take(chunksPerMa).ToArray())
- .TakeWhile(maSource => maSource.Length >= chunksPerMa)
- .Select(maSource => maSource.Select(c => c.Value).Average())
- .ToArray();
- }
- public SongData(SongData data, int msPerChunk, float chunkThreshold, int chunkDebounce, int chunksPerMa) : this(data.Samples, data.SampleRate, msPerChunk, chunkThreshold, chunkDebounce, chunksPerMa)
- {
- }
- }
- }
|