-
Notifications
You must be signed in to change notification settings - Fork 69
/
Resampler.cs
203 lines (165 loc) · 6.77 KB
/
Resampler.cs
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
using System;
using NWaves.Filters.Base;
using NWaves.Filters.Fda;
using NWaves.Signals;
using NWaves.Utils;
namespace NWaves.Operations
{
/// <summary>
/// Represents signal resampler (sampling rate converter).
/// </summary>
public class Resampler
{
/// <summary>
/// Gets or sets the order of lowpass anti-aliasing FIR filter
/// that will be created automatically if the filter is not specified explicitly.
/// By default, 101.
/// </summary>
public int MinResamplingFilterOrder { get; set; } = 101;
/// <summary>
/// Does interpolation of <paramref name="signal"/> followed by lowpass filtering.
/// </summary>
/// <param name="signal">Signal</param>
/// <param name="factor">Interpolation factor (e.g. factor=2 if 8000 Hz -> 16000 Hz)</param>
/// <param name="filter">Lowpass anti-aliasing filter</param>
public DiscreteSignal Interpolate(DiscreteSignal signal, int factor, FirFilter filter = null)
{
if (factor == 1)
{
return signal.Copy();
}
var output = new float[signal.Length * factor];
var pos = 0;
for (var i = 0; i < signal.Length; i++)
{
output[pos] = factor * signal[i];
pos += factor;
}
var lpFilter = filter;
if (filter is null)
{
var filterSize = factor > MinResamplingFilterOrder / 2 ?
2 * factor + 1 :
MinResamplingFilterOrder;
lpFilter = new FirFilter(DesignFilter.FirWinLp(filterSize, 0.5f / factor));
}
return lpFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate * factor, output));
}
/// <summary>
/// Does decimation of <paramref name="signal"/> preceded by lowpass filtering.
/// </summary>
/// <param name="signal">Signal</param>
/// <param name="factor">Decimation factor (e.g. factor=2 if 16000 Hz -> 8000 Hz)</param>
/// <param name="filter">Lowpass anti-aliasing filter</param>
public DiscreteSignal Decimate(DiscreteSignal signal, int factor, FirFilter filter = null)
{
if (factor == 1)
{
return signal.Copy();
}
var filterSize = factor > MinResamplingFilterOrder / 2 ?
2 * factor + 1 :
MinResamplingFilterOrder;
if (filter is null)
{
var lpFilter = new FirFilter(DesignFilter.FirWinLp(filterSize, 0.5f / factor));
signal = lpFilter.ApplyTo(signal);
}
var output = new float[signal.Length / factor];
var pos = 0;
for (var i = 0; i < output.Length; i++)
{
output[i] = signal[pos];
pos += factor;
}
return new DiscreteSignal(signal.SamplingRate / factor, output);
}
/// <summary>
/// Does band-limited resampling of <paramref name="signal"/>.
/// </summary>
/// <param name="signal">Signal</param>
/// <param name="newSamplingRate">Desired sampling rate</param>
/// <param name="filter">Lowpass anti-aliasing filter</param>
/// <param name="order">Order</param>
public DiscreteSignal Resample(DiscreteSignal signal,
int newSamplingRate,
FirFilter filter = null,
int order = 15)
{
if (signal.SamplingRate == newSamplingRate)
{
return signal.Copy();
}
var g = (float) newSamplingRate / signal.SamplingRate;
var input = signal.Samples;
var output = new float[(int)(input.Length * g)];
if (g < 1 && filter is null)
{
filter = new FirFilter(DesignFilter.FirWinLp(MinResamplingFilterOrder, g / 2));
input = filter.ApplyTo(signal).Samples;
}
var step = 1 / g;
for (var n = 0; n < output.Length; n++)
{
var x = n * step;
for (var i = -order; i < order; i++)
{
var j = (int) Math.Floor(x) - i;
if (j < 0 || j >= input.Length)
{
continue;
}
var t = x - j;
float w = (float) (0.5 * (1.0 + Math.Cos(t / order * Math.PI))); // Hann window
float sinc = (float) MathUtils.Sinc(t); // Sinc function
output[n] += w * sinc * input[j];
}
}
return new DiscreteSignal(newSamplingRate, output);
}
/// <summary>
/// Does simple resampling of <paramref name="signal"/> (as the combination of interpolation and decimation).
/// </summary>
/// <param name="signal">Input signal</param>
/// <param name="up">Interpolation factor</param>
/// <param name="down">Decimation factor</param>
/// <param name="filter">Lowpass anti-aliasing filter</param>
public DiscreteSignal ResampleUpDown(DiscreteSignal signal, int up, int down, FirFilter filter = null)
{
if (up == down)
{
return signal.Copy();
}
var newSamplingRate = signal.SamplingRate * up / down;
if (up > 20 && down > 20)
{
return Resample(signal, newSamplingRate, filter);
}
var output = new float[signal.Length * up];
var pos = 0;
for (var i = 0; i < signal.Length; i++)
{
output[pos] = up * signal[i];
pos += up;
}
var lpFilter = filter;
if (filter is null)
{
var factor = Math.Max(up, down);
var filterSize = factor > MinResamplingFilterOrder / 2 ?
8 * factor + 1 :
MinResamplingFilterOrder;
lpFilter = new FirFilter(DesignFilter.FirWinLp(filterSize, 0.5f / factor));
}
var upsampled = lpFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate * up, output));
output = new float[upsampled.Length / down];
pos = 0;
for (var i = 0; i < output.Length; i++)
{
output[i] = upsampled[pos];
pos += down;
}
return new DiscreteSignal(newSamplingRate, output);
}
}
}