forked from swharden/Spectrogram
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTestAGC.cs
More file actions
123 lines (98 loc) · 4.31 KB
/
TestAGC.cs
File metadata and controls
123 lines (98 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Spectrogram.Tests
{
class TestAGC
{
[Test]
public void Test_AGC_off()
{
string wavFilePath = "../../../../../data/qrss-10min.wav";
(double[] audio, int sampleRate) = AudioFile.ReadWAV(wavFilePath);
int fftSize = 8192;
var spec = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 2000, maxFreq: 3000);
spec.Add(audio);
spec.SaveImage("qrss-agc-off.png", intensity: 3);
}
[Test]
public void Test_AGC_normToNoiseFloor()
{
// strategy here is to normalize to the magnitude of the quietest 20% of frequencies
string wavFilePath = "../../../../../data/qrss-10min.wav";
(double[] audio, int sampleRate) = AudioFile.ReadWAV(wavFilePath);
int fftSize = 8192;
var spec = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 2000, maxFreq: 3000);
spec.Add(audio);
var ffts = spec.GetFFTs();
double normalIntensity = 2;
for (int i = 0; i < ffts.Count; i++)
{
double[] sorted = new double[ffts[i].Length];
ffts[i].CopyTo(sorted, 0);
Array.Sort(sorted);
double percentile = 0.25;
int percentileIndex = (int)(percentile * ffts[0].Length);
double floorValue = sorted[percentileIndex];
for (int y = 0; y < ffts[i].Length; y++)
{
ffts[i][y] = ffts[i][y] / floorValue * normalIntensity;
}
Console.WriteLine(floorValue);
}
spec.SaveImage("qrss-agc-norm-floor.png", intensity: 3);
}
[Test]
public void Test_AGC_normWindow()
{
// strategy here is to create a weighted moving window mean and normalize to that
string wavFilePath = "../../../../../data/qrss-10min.wav";
(double[] audio, int sampleRate) = AudioFile.ReadWAV(wavFilePath);
int fftSize = 8192;
var spec = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 2000, maxFreq: 3000);
spec.Add(audio);
var ffts = spec.GetFFTs();
for (int i = 0; i < ffts.Count; i++)
ffts[i] = SubtractMovingWindowFloor(ffts[i]);
spec.SaveImage("qrss-agc-norm-window.png", intensity: 3);
}
private double[] SubtractMovingWindow(double[] input, int windowSizePx = 100)
{
// return a copy of the input array with the moving window subtracted
var hanningWindow = new FftSharp.Windows.Hanning();
double[] window = hanningWindow.Create(windowSizePx);
double windowSum = window.Sum();
double[] windowed = new double[input.Length];
double[] normalized = new double[input.Length];
for (int i = 0; i < input.Length - window.Length; i++)
{
double windowedInputSum = 0;
for (int j = 0; j < window.Length; j++)
windowedInputSum += input[i + j] * window[j];
windowed[i + window.Length / 2] = windowedInputSum / windowSum;
}
for (int i = 0; i < input.Length; i++)
normalized[i] = Math.Max(input[i] - windowed[i], 0);
return normalized;
}
private double[] SubtractMovingWindowFloor(double[] input, int windowSizePx = 20, double percentile = .2)
{
// return a copy of the input with the noise floor subtracted
// where the noise floor is calculated from a moving window
// this is good but very slow
double[] normalized = new double[input.Length];
int floorIndex = (int)(percentile * windowSizePx);
double[] segment = new double[windowSizePx];
for (int i = 0; i < input.Length - windowSizePx; i++)
{
for (int j = 0; j < windowSizePx; j++)
segment[j] = input[i + j];
Array.Sort(segment);
normalized[i] = Math.Max(input[i] - segment[floorIndex], 0);
}
return normalized;
}
}
}