Skip to content

Instantly share code, notes, and snippets.

@detly
Created June 11, 2020 23:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save detly/990a13cdbf85a74bdea22580e6de6e0e to your computer and use it in GitHub Desktop.
Save detly/990a13cdbf85a74bdea22580e6de6e0e to your computer and use it in GitHub Desktop.
Gammatone weights
# Copyright 2014 Jason Heeris, jason.heeris@gmail.com
#
# This file is part of the gammatone toolkit, and is licensed under the 3-clause
# BSD license: https://github.com/detly/gammatone/blob/master/COPYING
"""
This module contains functions for calculating weights to approximate a
gammatone filterbank-like "spectrogram" from a Fourier transform.
"""
from __future__ import division
import numpy as np
import gammatone.filters as filters
import gammatone.gtgram as gtgram
def specgram_window(
nfft,
nwin,
):
"""
Window calculation used in specgram replacement function. Hann window of
width `nwin` centred in an array of width `nfft`.
"""
halflen = nwin // 2
halff = nfft // 2 # midpoint of win
acthalflen = int(np.floor(min(halff, halflen)))
halfwin = 0.5 * ( 1 + np.cos(np.pi * np.arange(0, halflen+1)/halflen))
win = np.zeros((nfft,))
win[halff:halff+acthalflen] = halfwin[0:acthalflen];
win[halff:halff-acthalflen:-1] = halfwin[0:acthalflen];
return win
def specgram(x, n, sr, w, h):
""" Substitute for Matlab's specgram, calculates a simple spectrogram.
:param x: The signal to analyse
:param n: The FFT length
:param sr: The sampling rate
:param w: The window length (see :func:`specgram_window`)
:param h: The hop size (must be greater than zero)
"""
# Based on Dan Ellis' myspecgram.m,v 1.1 2002/08/04
assert h > 0, "Must have a hop size greater than 0"
s = x.shape[0]
win = specgram_window(n, w)
c = 0
# pre-allocate output array
ncols = 1 + int(np.floor((s - n)/h))
d = np.zeros(((1 + n // 2), ncols), np.dtype(complex))
for b in range(0, s - n, h):
u = win * x[b : b + n]
t = np.fft.fft(u)
d[:, c] = t[0 : (1 + n // 2)].T
c = c + 1
return d
def fft_weights(
nfft,
fs,
nfilts,
width,
fmin,
fmax,
maxlen):
"""
:param nfft: the source FFT size
:param sr: sampling rate (Hz)
:param nfilts: the number of output bands required (default 64)
:param width: the constant width of each band in Bark (default 1)
:param fmin: lower limit of frequencies (Hz)
:param fmax: upper limit of frequencies (Hz)
:param maxlen: number of bins to truncate the rows to
:return: a tuple `weights`, `gain` with the calculated weight matrices and
gain vectors
Generate a matrix of weights to combine FFT bins into Gammatone bins.
Note about `maxlen` parameter: While wts has nfft columns, the second half
are all zero. Hence, aud spectrum is::
fft2gammatonemx(nfft,sr)*abs(fft(xincols,nfft))
`maxlen` truncates the rows to this many bins.
| (c) 2004-2009 Dan Ellis dpwe@ee.columbia.edu based on rastamat/audspec.m
| (c) 2012 Jason Heeris (Python implementation)
"""
ucirc = np.exp(1j * 2 * np.pi * np.arange(0, nfft / 2 + 1) / nfft)[None, ...]
# Common ERB filter code factored out
cf_array = filters.erb_space(fmin, fmax, nfilts)[::-1]
_, A11, A12, A13, A14, _, _, _, B2, gain = (
filters.make_erb_filters(fs, cf_array, width).T
)
A11, A12, A13, A14 = A11[..., None], A12[..., None], A13[..., None], A14[..., None]
r = np.sqrt(B2)
theta = 2 * np.pi * cf_array / fs
pole = (r * np.exp(1j * theta))[..., None]
GTord = 4
weights = np.zeros((nfilts, nfft))
weights[:, 0:ucirc.shape[1]] = (
np.abs(ucirc + A11 * fs) * np.abs(ucirc + A12 * fs)
* np.abs(ucirc + A13 * fs) * np.abs(ucirc + A14 * fs)
* np.abs(fs * (pole - ucirc) * (pole.conj() - ucirc)) ** (-GTord)
/ gain[..., None]
)
weights = weights[:, 0:int(maxlen)]
return weights, gain
def fft_gtgram(
wave,
fs,
window_time, hop_time,
channels,
f_min):
"""
Calculate a spectrogram-like time frequency magnitude array based on
an FFT-based approximation to gammatone subband filters.
A matrix of weightings is calculated (using :func:`gtgram.fft_weights`), and
applied to the FFT of the input signal (``wave``, using sample rate ``fs``).
The result is an approximation of full filtering using an ERB gammatone
filterbank (as per :func:`gtgram.gtgram`).
``f_min`` determines the frequency cutoff for the corresponding gammatone
filterbank. ``window_time`` and ``hop_time`` (both in seconds) are the size
and overlap of the spectrogram columns.
| 2009-02-23 Dan Ellis dpwe@ee.columbia.edu
|
| (c) 2013 Jason Heeris (Python implementation)
"""
width = 1 # Was a parameter in the MATLAB code
nfft = int(2 ** (np.ceil(np.log2(2 * window_time * fs))))
nwin, nhop, _ = gtgram.gtgram_strides(fs, window_time, hop_time, 0);
gt_weights, _ = fft_weights(
nfft,
fs,
channels,
width,
f_min,
fs / 2,
nfft / 2 + 1
)
sgram = specgram(wave, nfft, fs, nwin, nhop)
result = gt_weights.dot(np.abs(sgram)) / nfft
return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment