forked from avaneev/r8brain-free-src
-
Notifications
You must be signed in to change notification settings - Fork 0
/
r8butil.h
306 lines (259 loc) · 7.65 KB
/
r8butil.h
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
//$ nobt
//$ nocpp
/**
* @file r8butil.h
*
* @brief The inclusion file with several utility functions.
*
* This file includes several utility functions used by various utility
* programs like "calcErrorTable.cpp".
*
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
* See the "License.txt" file for license.
*/
#ifndef R8BUTIL_INCLUDED
#define R8BUTIL_INCLUDED
#include "r8bbase.h"
namespace r8b {
/**
* @param re Real part of the frequency response.
* @param im Imaginary part of the frequency response.
* @return A magnitude response value converted from the linear scale to the
* logarithmic scale.
*/
inline double convertResponseToLog( const double re, const double im )
{
return( 4.34294481903251828 * log( re * re + im * im + 1e-100 ));
}
/**
* An utility function that performs frequency response scanning step update
* based on the current magnitude response's slope.
*
* @param[in,out] step The current scanning step. Will be updated on
* function's return. Must be a positive value.
* @param curg Squared magnitude response at the current frequency point.
* @param[in,out] prevg_log Previous magnitude response, log scale. Will be
* updated on function's return.
* @param prec Precision multiplier, affects the size of the step.
* @param maxstep The maximal allowed step.
* @param minstep The minimal allowed step.
*/
inline void updateScanStep( double& step, const double curg,
double& prevg_log, const double prec, const double maxstep,
const double minstep = 1e-11 )
{
double curg_log = 4.34294481903251828 * log( curg + 1e-100 );
curg_log += ( prevg_log - curg_log ) * 0.7;
const double slope = fabs( curg_log - prevg_log );
prevg_log = curg_log;
if( slope > 0.0 )
{
step /= prec * slope;
step = max( min( step, maxstep ), minstep );
}
}
/**
* Function locates normalized frequency at which the minimum filter gain
* is reached. The scanning is performed from lower (left) to higher
* (right) frequencies, the whole range is scanned.
*
* Function expects that the magnitude response is always reducing from lower
* to high frequencies, starting at "minth".
*
* @param flt Filter response.
* @param fltlen Filter response's length in samples (taps).
* @param[out] ming The current minimal gain (squared). On function's return
* will contain the minimal gain value found (squared).
* @param[out] minth The normalized frequency where the minimal gain is
* currently at. On function's return will point to the normalized frequency
* where the new minimum was found.
* @param thend The ending frequency, inclusive.
*/
inline void findFIRFilterResponseMinLtoR( const double* const flt,
const int fltlen, double& ming, double& minth, const double thend )
{
const double maxstep = minth * 2e-3;
double curth = minth;
double re;
double im;
calcFIRFilterResponse( flt, fltlen, M_PI * curth, re, im );
double prevg_log = convertResponseToLog( re, im );
double step = 1e-11;
while( true )
{
curth += step;
if( curth > thend )
{
break;
}
calcFIRFilterResponse( flt, fltlen, M_PI * curth, re, im );
const double curg = re * re + im * im;
if( curg > ming )
{
ming = curg;
minth = curth;
break;
}
ming = curg;
minth = curth;
updateScanStep( step, curg, prevg_log, 0.31, maxstep );
}
}
/**
* Function locates normalized frequency at which the maximal filter gain
* is reached. The scanning is performed from lower (left) to higher
* (right) frequencies, the whole range is scanned.
*
* Note: this function may "stall" in very rare cases if the magnitude
* response happens to be "saw-tooth" like, requiring a very small stepping to
* be used. If this happens, it may take dozens of seconds to complete.
*
* @param flt Filter response.
* @param fltlen Filter response's length in samples (taps).
* @param[out] maxg The current maximal gain (squared). On function's return
* will contain the maximal gain value (squared).
* @param[out] maxth The normalized frequency where the maximal gain is
* currently at. On function's return will point to the normalized frequency
* where the maximum was reached.
* @param thend The ending frequency, inclusive.
*/
inline void findFIRFilterResponseMaxLtoR( const double* const flt,
const int fltlen, double& maxg, double& maxth, const double thend )
{
const double maxstep = maxth * 1e-4;
double premaxth = maxth;
double premaxg = maxg;
double postmaxth = maxth;
double postmaxg = maxg;
double prevth = maxth;
double prevg = maxg;
double curth = maxth;
double re;
double im;
calcFIRFilterResponse( flt, fltlen, M_PI * curth, re, im );
double prevg_log = convertResponseToLog( re, im );
double step = 1e-11;
bool WasPeak = false;
int AfterPeakCount = 0;
while( true )
{
curth += step;
if( curth > thend )
{
break;
}
calcFIRFilterResponse( flt, fltlen, M_PI * curth, re, im );
const double curg = re * re + im * im;
if( curg > maxg )
{
premaxth = prevth;
premaxg = prevg;
maxg = curg;
maxth = curth;
WasPeak = true;
AfterPeakCount = 0;
}
else
if( WasPeak )
{
if( AfterPeakCount == 0 )
{
postmaxth = curth;
postmaxg = curg;
}
if( AfterPeakCount == 5 )
{
// Perform 2 approximate binary searches.
int k;
for( k = 0; k < 2; k++ )
{
double l = ( k == 0 ? premaxth : maxth );
double curgl = ( k == 0 ? premaxg : maxg );
double r = ( k == 0 ? maxth : postmaxth );
double curgr = ( k == 0 ? maxg : postmaxg );
while( true )
{
const double c = ( l + r ) * 0.5;
calcFIRFilterResponse( flt, fltlen, M_PI * c,
re, im );
const double curg = re * re + im * im;
if( curgl > curgr )
{
r = c;
curgr = curg;
}
else
{
l = c;
curgl = curg;
}
if( r - l < 1e-11 )
{
if( curgl > curgr )
{
maxth = l;
maxg = curgl;
}
else
{
maxth = r;
maxg = curgr;
}
break;
}
}
}
break;
}
AfterPeakCount++;
}
prevth = curth;
prevg = curg;
updateScanStep( step, curg, prevg_log, 1.0, maxstep );
}
}
/**
* Function locates normalized frequency at which the specified maximum
* filter gain is reached. The scanning is performed from higher (right)
* to lower (left) frequencies, scanning stops when the required gain
* value was crossed. Function uses an extremely efficient binary search and
* thus expects that the magnitude response has the "main lobe" form produced
* by windowing, with a minimal pass-band ripple.
*
* @param flt Filter response.
* @param fltlen Filter response's length in samples (taps).
* @param maxg Maximal gain (squared).
* @param[out] th The current normalized frequency. On function's return will
* point to the normalized frequency where "maxg" is reached.
* @param thend The leftmost frequency to scan, inclusive.
*/
inline void findFIRFilterResponseLevelRtoL( const double* const flt,
const int fltlen, const double maxg, double& th, const double thend )
{
// Perform exact binary search.
double l = thend;
double r = th;
while( true )
{
const double c = ( l + r ) * 0.5;
if( r - l < 1e-14 )
{
th = c;
break;
}
double re;
double im;
calcFIRFilterResponse( flt, fltlen, M_PI * c, re, im );
const double curg = re * re + im * im;
if( curg > maxg )
{
l = c;
}
else
{
r = c;
}
}
}
} // namespace r8b
#endif // R8BUTIL_INCLUDED