using SongVocalSectionAnalyser.Common.Collections; using System; using System.Collections.Generic; using System.Linq; namespace SongVocalSectionAnalyser.AudioAnalysis { public class SongData { public IReadOnlyList Samples { get; } public IReadOnlyList Chunks { get; } public IReadOnlyList MaLine { get; } public int SampleRate { get; } public int SamplePerChunk { get; } public int ChunksPerMa { get; } public SongData(IReadOnlyList 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) { } } }