summaryrefslogtreecommitdiff
path: root/src/loris/KaiserWindow.C
blob: c7f3952a42eea08cc4a1d090bd8016e6d1e09d57 (plain)
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
/*
 * This is the Loris C++ Class Library, implementing analysis, 
 * manipulation, and synthesis of digitized sounds using the Reassigned 
 * Bandwidth-Enhanced Additive Sound Model.
 *
 * Loris is Copyright (c) 1999-2010 by Kelly Fitz and Lippold Haken
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY, without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * KaiserWindow.C
 *
 * Implementation of class Loris::KaiserWindow.
 *
 * Kelly Fitz, 14 Dec 1999
 * loris@cerlsoundgroup.org
 *
 * http://www.cerlsoundgroup.org/Loris/
 *
 */

#if HAVE_CONFIG_H
    #include "config.h"
#endif

#include "KaiserWindow.h"
#include "LorisExceptions.h"
#include <cmath>

#if defined(HAVE_M_PI) && (HAVE_M_PI)
    const double Pi = M_PI;
#else
    const double Pi = 3.14159265358979324;
#endif

using namespace std;

//  begin namespace
namespace Loris {

//  prototypes for static helpers, defined below
static double zeroethOrderBessel( double x );
static double firstOrderBessel( double x );

// ---------------------------------------------------------------------------
//  buildWindow
// ---------------------------------------------------------------------------
//! Build a new Kaiser analysis window having the specified shaping
//! parameter. See Oppenheim and Schafer:  "Digital Signal Processing" 
//! (1975), p. 452 for further explanation of the Kaiser window. Also, 
//! see Kaiser and Schafer, 1980.
//!
//! \param      win is the vector that will store the window
//!             samples. The number of samples computed will be
//!             equal to the length of this vector. Any previous
//!             contents will be overwritten.
//! \param      shape is the Kaiser shaping parameter, controlling
//!             the sidelobe rejection level.
//
void
KaiserWindow::buildWindow( vector< double > & win, double shape )
{   
    //  Pre-compute the shared denominator in the Kaiser equation. 
    const double oneOverDenom = 1.0 / zeroethOrderBessel( shape );
  
    const unsigned int N = win.size() - 1;
    const double oneOverN = 1.0 / N;
    
    for ( unsigned int n = 0; n <= N; ++n )
    {
        const double K = (2.0 * n * oneOverN) - 1.0;
        const double arg = sqrt( 1.0 - (K * K) );
        
        win[n] = zeroethOrderBessel( shape * arg ) * oneOverDenom;
    }
}

// ---------------------------------------------------------------------------
//  createDerivativeWindow
// ---------------------------------------------------------------------------
//! Build a new time-derivative Kaiser analysis window having the
//! specified shaping parameter, for computing frequency reassignment.
//! The closed form for the time derivative can be obtained from the
//! property of modified Bessel functions that the derivative of the
//! zeroeth order function is equal to the first order function.
//!
//! \param      win is the vector that will store the window
//!             samples. The number of samples computed will be
//!             equal to the length of this vector. Any previous
//!             contents will be overwritten.
//! \param      shape is the Kaiser shaping parameter, controlling
//!             the sidelobe rejection level.
//
void
KaiserWindow::buildTimeDerivativeWindow( vector< double > & win, double shape )
{   
    //  Pre-compute the common factor that does not depend on n.
    const unsigned int N = win.size() - 1;
    const double oneOverN = 1.0 / N;
    
    const double commonFac = - 2.0 * shape / (N * zeroethOrderBessel( shape ) );
  
    //  w'[0] = w'[N] = 0
    win[0] = win[N] = 0.0;
    
    for ( unsigned int n = 1; n < N; ++n )
    {
        const double K = (2.0 * n * oneOverN) - 1.0;
        const double arg = sqrt( 1.0 - (K * K) );
        
        win[n] = commonFac * firstOrderBessel( shape * arg ) * K / arg;
    }
}


// ---------------------------------------------------------------------------
//  zeroethOrderBessel
// ---------------------------------------------------------------------------
//  Compute the zeroeth order modified Bessel function of the first kind 
//  at x using the series expansion, used to compute the Kasier window
//  function.
//
static double zeroethOrderBessel( double x )
{
    const double eps = 0.000001;
    
    //  initialize the series term for m=0 and the result
    double besselValue = 0;
    double term = 1;
    double m = 0;
    
    //  accumulate terms as long as they are significant
    while(term  > eps * besselValue)
    {
        besselValue += term;
        
        //  update the term
        ++m;
        term *= (x*x) / (4*m*m);
    }
    
    return besselValue;
}

// ---------------------------------------------------------------------------
//  firstOrderBessel
// ---------------------------------------------------------------------------
//  Compute the first order modified Bessel function of the first kind 
//  at x using the series expansion, used to compute the time derivative
//  of the Kasier window function for computing frequency reassignment.
//
static double firstOrderBessel( double x )
{
    const double eps = 0.000001;
    
    //  initialize the series term for m=0 and the result
    double besselValue = 0;
    double term = .5*x;
    double m = 0;
    
    //  accumulate terms as long as they are significant
    while(term  > eps * besselValue)
    {
        besselValue += term;
        
        //  update the term
        ++m;
        term *= (x*x) / (4*m*(m+1));
    }
    
    return besselValue;
}

// ---------------------------------------------------------------------------
//  computeShape
// ---------------------------------------------------------------------------
//  Compute the Kaiser window shaping parameter from the specified attenuation 
//  of side lobes. This algorithm is given in Kaiser an Schafer,1980 and is 
//  supposed to give better than 0.36% accuracy (Kaiser and Schafer 1980).
//
double
KaiserWindow::computeShape( double atten )
{
    if ( atten < 0. )
    {
        Throw( InvalidArgument, 
        	   "Kaiser window shape must be computed from positive (> 0dB)"
        	   " sidelobe attenuation. (received attenuation < 0)" );
	}
	
    double alpha;
    
    if ( atten > 60.0 )
    {
        alpha = 0.12438 * (atten + 6.3);
    }
    else if ( atten > 13.26 )
    {
        alpha = 0.76609L * ( pow((atten - 13.26), 0.4) ) + 
                            0.09834L * (atten - 13.26L);
    }
    else
    {
        //  can't have less than 13dB.
        alpha = 0.0;
    }
    
    return alpha;
}
// ---------------------------------------------------------------------------
//  computeLength
// ---------------------------------------------------------------------------
//  Compute the length (in samples) of the Kaiser window from the desired 
//  (approximate) main lobe width and the control parameter. Of course, since 
//  the window must be an integer number of samples in length, your actual 
//  lobal mileage may vary. This equation appears in Kaiser and Schafer 1980
//  (on the use of the I0 window class for spectral analysis) as Equation 9.
//
//  The main width of the main lobe must be normalized by the sample rate,
//  that is, it is a fraction of the sample rate.
//
unsigned long
KaiserWindow::computeLength( double width, double alpha )
{
    //double alpha = computeShape( atten );

    //  The last 0.5 is cheap rounding.
    //  But I think I don't need cheap rounding because the equation 
    //  from Kaiser and Schafer has a +1 that appears to be a cheap
    //  ceiling function.
    return long(1.0 + (2. * sqrt((Pi*Pi) + (alpha*alpha)) / (Pi * width)) /* + 0.5 */);
}

}   //  end of namespace Loris