67    Calculate integral of (part of) Hann window. 
   68    If the args are vectors, the return value will match those. 
   71        x1: lower bound [-0.5, 0.5] 
   72        x2: upper bound [-0.5, 0.5] 
   74        Integral of Hann window between x1 and x2 
   76    x1 = np.clip(x1, -0.5, 0.5)
 
   77    x2 = np.clip(x2, -0.5, 0.5)
 
   78    return (sin(2*pi*x2) - sin(2*pi*x1))/(2*pi) + (x2-x1)
 
 
   84        freq: array of frequencies of data points [Hz] - equally spaced 
   88        freq: array frequencies of data points [Hz] 
   89        Q: matrix to smooth, power:  {fsm} = [Q] * {fraw} 
   91    Warning: this method does not work on levels (dB) 
   93    According to Tylka_JAES_SmoothingWeights.pdf 
   94    "A Generalized Method for Fractional-Octave Smoothing of Transfer Functions 
   95    that Preserves Log-Frequency Symmetry" 
   96    https://doi.org/10.17743/jaes.2016.0053 
  102    assert Noct > 0, 
"'Noct' must be absolute positive" 
  103    assert np.isclose(freq[-1]-freq[-2], freq[1]-freq[0]), 
"Input data must "\
 
  104        "have a linear frequency spacing" 
  106        raise Warning(
'Check if \'Noct\' is entered correctly')
 
  110    Q = np.zeros(shape=(L, L), dtype=np.float16)  
 
  112    x0 = 1 
if freq[0] == 0 
else 0  
 
  115    ifreq = freq/(freq[1]-freq[0])  
 
  116    ifreq = np.array(ifreq.astype(int))
 
  118    ifreqMax = ifreq[L-1]    
 
  119    sfact = 2**((1/Noct)/2)  
 
  121    kpmin = np.floor(ifreq/sfact).astype(int)  
 
  122    kpmax = np.ceil(ifreq*sfact).astype(int)   
 
  124    for ff 
in range(x0, L):  
 
  127        if kpmin[ff] < ifreqMin:
 
  129            kpmax[ff] = np.ceil(ifreq[ff]**2/ifreqMin)  
 
  130            if np.isclose(kpmin[ff], kpmax[ff]):
 
  132            NoctAct = 1/log2(kpmax[ff]/kpmin[ff])
 
  133        elif kpmax[ff] > ifreqMax:
 
  134            kpmin[ff] = np.floor(ifreq[ff]**2/ifreqMax)  
 
  136            if np.isclose(kpmin[ff], kpmax[ff]):
 
  138            NoctAct = 1/log2(kpmax[ff]/kpmin[ff])
 
  142        kp = np.arange(kpmin[ff], kpmax[ff]+1)       
 
  143        Phi1 = log2((kp - 0.5)/ifreq[ff]) * NoctAct  
 
  144        Phi2 = log2((kp + 0.5)/ifreq[ff]) * NoctAct
 
  146        Q[ff, kpmin[ff]-ifreq[0]:kpmax[ff]-ifreq[0]+1] = W
 
  149    Qpower = np.sum(Q, axis=0)
 
  150    Q = Q / Qpower[np.newaxis, :]
 
 
  156                       st: SmoothingType = SmoothingType.levels):
 
  158    Apply fractional octave smoothing to data in the frequency domain. 
  161        freq: array of frequencies of data points [Hz] - equally spaced 
  162        M: array of data, either power or dB 
  163        the smoothing type `st`, the smoothing is applied. 
  165        st: smoothing type = data type of input data 
  168        freq : array frequencies of data points [Hz] 
  169        Msm : float smoothed magnitude of data points 
  172    MM = copy.deepcopy(M)
 
  174    assert len(MM) > 0, 
"Smoothing function: input array is empty"   
  175    assert Noct > 0, 
"'Noct' must be absolute positive" 
  177        raise Warning(
'Check if \'Noct\' is entered correctly')
 
  178    assert len(freq) == len(MM), 
"f and M should have equal length" 
  180    if st == SmoothingType.ps:
 
  181        assert np.min(MM) >= 0, 
'Power spectrum values cannot be negative' 
  182    if st == SmoothingType.levels 
and isinstance(MM.dtype, complex):
 
  183        raise RuntimeError(
'Decibel input should be real-valued')
 
  186    if st == SmoothingType.levels:
 
  188    elif st == SmoothingType.ps:
 
  191        raise RuntimeError(f
"Incorrect SmoothingType: {st}")
 
  194    Psm = np.zeros_like(P)         
 
  201    nfft = int(2*(len(freq)-1))
 
  202    key = f
"nfft{nfft}_Noct{Noct}"   
  204    if 'Qdict' not in globals():  
 
  215    Psm = np.matmul(Q, P)
 
  218    if st == SmoothingType.levels:
 
  219        Psm = 10*np.log10(Psm)