#!/usr/bin/python
# copyright (C) 2011 - 2013 Jean-Louis Durrieu
#
"""Constant-Q transform after the work by C. Scholkhuber and A. Klapuri
2010 [SK2010]_
Adaptation of the Constant Q transform as presented in
.. [SK2010] Schoerkhuber, C. and Klapuri, A.,
\"Constant-Q transform toolbox for music processing,\"
submitted to the 7th Sound and Music Computing Conference, Barcelona, Spain.
Comments beginning with '%' and '%%' are retained from the original Matlab
code.
Python/Numpy/Scipy by
Jean-Louis Durrieu, EPFL, 2012 - 2013
"""
import numpy as np
import scipy.signal as spsig # for the windows and filters
# import scipy.sparse as spspa
import scipy.interpolate as spinterp
# from .. import audioObject as ao # for the stft and istft
from stft import stft, istft
from ..tools.utils import nextpow2, sqrt_blackmanharris
[docs]class CQTKernel(object):
"""The CQT Kernel contains everything that can be
precomputed for Constant-Q transforms. This
relies on [SK2010]_, and therefore computes a Kernel
for a single octave. It is then efficiently used to
compute the decomposition on the different octaves by
downsampling the signal.
Parameters:
fmax
The maximum desired central frequency
bins
The number of bins per octave
fs
Sampling rate of the audio files
q
parameter that controls the quality
atomHopFactor
hopsize rate (0.25 is a hopsize of 25% the size of the windows)
between successive analysis windows
thresh
threshold value for sparsifying the kernel
(Note: in this implementation, we do not use the sparsity, more
efficiency could be achieved by considering it)
winFunc (python function that outputs an array)
the analysis window function
perfRast
whether computing rasterized version or not
(if so, the decompositions at all scales will have the same
number of frames, otherwise, each lower analysis octave will have
half as many frames as the direct upper analysis octave.)
Attributes:
sparKernel
weight
atomHOP
FFTLen
fftOLP
fftHOP
bins
winNr
Nk_max
Q
fmin
fmax
frequencies
perfRast
first_center
fs
winFunc
thresh
q
"""
def __init__(self, fmax, bins, fs,
q = 1,
atomHopFactor = 0.25,
thresh = 0.0005,
winFunc = sqrt_blackmanharris,
perfRast = 0
):
"""
Generate the CQT kernel for one octave
"""
# %% define
# checking that fmax<fs/2
if fmax >= fs/2.:
raise ValueError("fmax (%s) is too big for fs (%s)"
%(str(fmax), str(fs)))
# fmax = np.minimum(fmax, fs/2.)
fmin = (fmax / 2.) * (2 ** (1./bins))
Q = 1. / (2 ** (1./bins) - 1)
Q = Q * q
Nk_max = Q * fs / fmin
Nk_max = np.round(Nk_max) # length of the largest atom [samples]
# %% Compute FFT size, FFT hop, atom hop, ...
# % length of the shortest atom [samples]
Nk_min = np.round( Q * fs /
(fmin * (2**((bins-1.)/bins))) )
# %atom hop size
# DJL: nextpow2, so that at all octaves, the frames are centered on
# integer valued samples of the (decimated) signal.
# atomHOP = np.round(Nk_min * atomHopFactor)
atomHOP = nextpow2(Nk_min * atomHopFactor)/2
# %first possible center position within the frame
first_center = np.ceil(Nk_max / 2.)
# %lock the first center to an integer multiple of the atom hop size
first_center = atomHOP * np.ceil(first_center * 1. / atomHOP)
# %use smallest possible FFT size (increase sparsity)
FFTLen = nextpow2(first_center + np.ceil(Nk_max / 2.))
## DEBUG DEBUG DEBUG
## print "FFTLen", FFTLen, "Nk_min", Nk_min
if perfRast:
# %number of temporal atoms per FFT Frame
# DJL: 20130314: why is this different from
# non rasterized transform? adding 1 again, but has to be
# further studied...
winNr = np.floor((FFTLen - np.ceil(Nk_max / 2.) -
first_center) / atomHOP) + 1
if winNr == 0:
FFTLen = FFTLen * 2
winNr = np.floor((FFTLen - np.ceil(Nk_max / 2.)-
first_center) / atomHOP)
else:
# %number of temporal atoms per FFT Frame
winNr = np.floor((FFTLen - np.ceil(Nk_max / 2.) -
first_center) / atomHOP) + 1
last_center = first_center + (winNr - 1.) * atomHOP
# % hop size of FFT frames
fftHOP = (last_center + atomHOP) - first_center
# %overlap of FFT frames in percent
fftOLP = (FFTLen - fftHOP) * (1. / FFTLen)*100.
# %% init variables
tempKernel = np.zeros(FFTLen, dtype=np.complex)
sparKernel = np.zeros([bins * winNr, FFTLen],
dtype=np.complex)
frequencies = []
# %% Compute kernel
atomInd = 0
for k in np.arange(bins):
Nk = np.round(Q * fs /
(fmin * (2**((k*1.)/bins))))
# print Nk
winFct = winFunc(Nk)
fk = fmin * (2**((k*1.)/bins))
frequencies.append(fk)
tempKernelBin = (
(winFct *1. / Nk) *
np.exp(2 * np.pi * 1j * fk * np.arange(Nk) / fs)
)
atomOffset = first_center - np.ceil(Nk / 2.)
for i in np.arange(winNr):
shift = atomOffset + i * atomHOP
# print shift
tempKernel[shift:(Nk+shift)] = tempKernelBin
atomInd = atomInd + 1
specKernel= np.fft.fft(tempKernel)
specKernel[np.abs(specKernel)<=thresh] = 0
# sparsifying this?
# sparKernel = sparse([sparKernel; specKernel])
sparKernel[int(i + k * winNr)] = specKernel
# %reset window
tempKernel = np.zeros(FFTLen, dtype=np.complex)
sparKernel = (sparKernel.T) * 1. / FFTLen
# %% Normalize the magnitudes of the atoms
wx1 = np.argmax(sparKernel[:, 0])
wx2 = np.argmax(sparKernel[:,-1])
wK = sparKernel[wx1:wx2,:]
wK = np.diag(np.dot(wK, np.conjugate(wK.T)))
wK = wK[int(np.round(1./q)):\
int(len(wK) - np.round(1./q) - 1)]
weight = 1. / np.mean(np.abs(wK))
weight *= (fftHOP * 1. / FFTLen)
# %sqrt because the same weight is applied in icqt again
weight = np.sqrt(weight)
sparKernel *= weight
self.sparKernel = np.ascontiguousarray(sparKernel)
self.weight = weight
self.atomHOP = atomHOP
self.FFTLen = FFTLen
self.fftOLP = fftOLP
self.fftHOP = fftHOP
self.bins = bins
self.winNr = winNr
self.Nk_max = Nk_max
self.Q = Q
self.fmin = fmin
self.fmax = fmax
self.frequencies = frequencies
self.perfRast = perfRast
self.first_center = first_center
self.fs = fs
self.winFunc = winFunc
self.thresh = thresh
self.q = q
def __str__(self):
description = "CQT Kernel structure, containing:\n"
for k, v in self.__dict__.items():
description += str(k) + ': ' + str(v) + '\n'
return description
[docs]class HybridCQTKernel(CQTKernel):
"""Hybrid CQT/Linear kernel
"""
def __init__(self, **kwargs):
super(HybridCQTKernel, self).__init__(**kwargs)
self.computeMissingLinearFreqKernel()
[docs] def computeMissingLinearFreqKernel(self):
"""Compute the missing (high) frequency
components, and make a similar Kernel for them.
We can use this for the first octave (the highest frequency octave)
to extend the high frequencies. Actually, this can be used to compute
a hybrid CQT transform on the low frequencies, while keeping linear
freqs in the high spectrum, and still benefiting from the invertibility
of the CQT transform by Schoerkhuber and Klapuri
"""
# %% init variables
tempKernel = np.zeros(self.FFTLen,
dtype=np.complex)
Nk = self.Nk_max # self.FFTLen # self.Nk_max
pfirst = int(self.fmax*(2.**(1./self.bins))/self.fs*Nk)
# *(2.**((self.bins-1)/self.bins))/self.fs*Nk)
plast = int(Nk/2.) + 1
first_center = np.ceil(Nk / 2.)
self.linBins = plast - pfirst
sparKernel = np.zeros([self.linBins * self.winNr, self.FFTLen],
dtype=np.complex)
# %% Compute kernel
for p in np.arange(pfirst, plast):
# print Nk
winFct = self.winFunc(Nk)
fk = p * 1. / Nk * self.fs# fmin * (2**((k*1.)/bins))
self.frequencies.append(fk)
tempKernelBin = (
(winFct * 1. / Nk) *
np.exp(2 * np.pi * 1j * p * np.arange(Nk) / Nk)
) # Fourier exponential function!
atomOffset = first_center - np.ceil(Nk / 2.);
for i in np.arange(self.winNr):
shift = atomOffset + i * self.atomHOP
## print shift, Nk
tempKernel[shift:(Nk+shift)] = tempKernelBin
specKernel= np.fft.fft(tempKernel)
specKernel[np.abs(specKernel)<=self.thresh] = 0
# sparsifying this?
## sparKernel = sparse([sparKernel; specKernel])
## print i, p, int(i + p * self.winNr)
sparKernel[int(i + (p-pfirst) * self.winNr)] = specKernel
# %reset window
tempKernel = np.zeros(self.FFTLen, dtype=np.complex)
sparKernel = (sparKernel.T) * 1. / self.FFTLen
# %% Normalize the magnitudes of the atoms
wx1 = np.argmax(sparKernel[:,0])
wx2 = np.argmax(sparKernel[:,-1])
wK = sparKernel[wx1:wx2,:]
wK = np.diag(np.dot(wK, np.conjugate(wK.T)))
wK = wK[int(np.round(1./self.q)):\
int(len(wK) - np.round(1./self.q) - 1)]
weight = 1. / np.mean(np.abs(wK))
weight *= (self.fftHOP * 1. / self.FFTLen)
# %sqrt because the same weight is applied in icqt again
weight = np.sqrt(weight)
sparKernel *= weight
self.linearSparKernel = np.ascontiguousarray(sparKernel)
[docs]class MinQTKernel(CQTKernel):
"""Min Q Transform Kernel
"""
def __init__(self, bins, fmax, fs, linFTLen=2048,
**kwargs):
"""Initiates and computes the MinQT kernel,
by determining the frequency of split - fmax - and having 2 different
frequency ranges (log for CQT part and lin for linear part)
"""
Q = 1./(2**(1./bins)-1)
Kmax = int(np.ceil(Q))
fmax = 2**(-1./bins) * Kmax * fs * 1. / linFTLen
self.Q = Q # minimum Q value for the whole transform
self.Kmax = Kmax
self.linFTLen = linFTLen
self.fs = fs
self.fmax = fmax
self.bins = bins
super(MinQTKernel, self).__init__(fmax=self.fmax, fs=self.fs,
bins=self.bins,
**kwargs)
# instead of linear Kernel, compute with FFT
# the number of additional linear freq. bins:
self.linBins = linFTLen/2 - Kmax + 1
self.linWindow = self.winFunc(linFTLen)
## legacy: don't use the following kernel, this is too slow!
# self.computeMissingLinearFreqKernel()
[docs] def computeMissingLinearFreqKernel(self):
"""Compute the missing (high) frequency
components, and make a similar Kernel for them.
We can use this for the first octave (the highest frequency octave)
to extend the high frequencies. Actually, this can be used to compute
a hybrid CQT transform on the low frequencies, while keeping linear
freqs in the high spectrum, and still benefiting from the invertibility
of the CQT transform by Schoerkhuber and Klapuri
"""
# %% init variables
tempKernel = np.zeros(self.linFTLen,
dtype=np.complex)
N = self.linFTLen
pfirst = int(self.Kmax)
plast = int(N/2 + 1)
first_center = np.ceil(N / 2.)
self.linBins = plast - pfirst
sparKernel = np.zeros([self.linBins, self.linFTLen],
dtype=np.complex)
# %% Compute kernel
winFct = self.winFunc(N)
for p in np.arange(pfirst, plast):
fk = p * 1. / N * self.fs# fmin * (2**((k*1.)/bins))
self.frequencies.append(fk)
tempKernelBin = (
(winFct * 1. / N) *
np.exp(2 * np.pi * 1j * p * np.arange(N) / N)
) # Fourier exponential function!
# to have time aligned atoms:
atomOffset = first_center - np.ceil(N / 2.);
i = 0 # only one window per frame of FT for linear part
shift = atomOffset
tempKernel[shift:(Nk+shift)] = tempKernelBin
specKernel= np.fft.fft(tempKernel)
specKernel[np.abs(specKernel)<=self.thresh] = 0
# sparsifying this?
## sparKernel = sparse([sparKernel; specKernel])
## print i, p, int(i + p * self.winNr)
sparKernel[int(i + (p-pfirst) * self.winNr)] = specKernel
# %reset window
tempKernel = np.zeros(self.FFTLen, dtype=np.complex)
sparKernel = (sparKernel.T) * 1. / self.FFTLen
# %% Normalize the magnitudes of the atoms
wx1 = np.argmax(sparKernel[:,0])
wx2 = np.argmax(sparKernel[:,-1])
wK = sparKernel[wx1:wx2,:]
wK = np.diag(np.dot(wK, np.conjugate(wK.T)))
wK = wK[int(np.round(1./self.q)):\
int(len(wK) - np.round(1./self.q) - 1)]
weight = 1. / np.mean(np.abs(wK))
weight *= (self.fftHOP * 1. / self.FFTLen)
# %sqrt because the same weight is applied in icqt again
weight = np.sqrt(weight)
sparKernel *= weight
self.linearSparKernel = np.ascontiguousarray(sparKernel)
[docs]class CQTransfo(object):
"""Constant Q Transform"""
transformname = 'cqt'
def __init__(self,
fmin, fmax, bins, fs,
q=1,
atomHopFactor=0.25,
thresh=0.0005,
winFunc=sqrt_blackmanharris,
perfRast=0,
cqtkernel=None,
lowPassCoeffs=None,
data=None,
verbose=0, **kwargs):
"""cqt
from the reference implementation by C. Scholkhuber and A. Klapuri
Matlab program downloaded from
http://www.elec.qmul.ac.uk/people/anssik/cqt/
on 22/08/2012
"""
self.verbose = verbose
self.fmin = fmin
self.fmax = fmax
self.bins = bins # bins per octave
self.bpo = bins
self.fs = fs
self.q = q
self.atomHopFactor = atomHopFactor
self.thresh = thresh
if winFunc is None:
winFunc = sqrt_blackmanharris
self.winFunc = winFunc
self.perfRast = perfRast
# % % define
self.octaveNr = np.ceil(np.log2(fmax * 1. / fmin))
self.freqbins = bins * self.octaveNr
# %set fmin to actual value
self.fmin = (fmax / (2**self.octaveNr)) * 2**(1./bins)
# %% design lowpass filter
if lowPassCoeffs is None:
self.LPorder = 6 # %order of the anti-aliasing filter
self.cutoff = 0.5
# %design f_nyquist/2-lowpass filter
self.B, self.A = spsig.butter(N=self.LPorder,
Wn=self.cutoff,
btype='low')
# %% design kernel for one octave
if cqtkernel is None:
self.cqtkernel = CQTKernel(
fmax=fmax,
bins=bins,
fs=fs,
q=q,
atomHopFactor=atomHopFactor,
thresh=thresh,
winFunc=winFunc,
perfRast=perfRast)
else:
self.cqtkernel = cqtkernel
if data is not None:
self.computeTransform(data=data) # more general from subclasses
def computeCQT(self, data):
# %% calculate CQT
if self.verbose: print "Computing CQT"
if hasattr(self, '_spCQT'):
del self._spCQT
cqtkernel = self.cqtkernel
self.datalen_init = data.shape[0]
self.cellCQT = {} # contrary to CSAK original code, keep this always
self.maxBlock = (
cqtkernel.FFTLen *
(2**(self.octaveNr-1))) # %largest FFT Block (virtual)
self.suffixZeros = self.maxBlock
self.prefixZeros = self.maxBlock
# %zeropadding
x = (
np.concatenate([np.zeros(self.prefixZeros),
data,
np.zeros(self.suffixZeros)])
)
OVRLP = cqtkernel.FFTLen - cqtkernel.fftHOP
# %conjugate spectral kernel for cqt transformation
K = np.ascontiguousarray(np.conjugate(cqtkernel.sparKernel.T))
self.nframes = []
if not self.cqtkernel.perfRast: # this is working well enough but...
for i in np.arange(self.octaveNr):
if self.verbose:
print " Octave n.", i, "out of", self.octaveNr
# %generating FFT blocks
nframes = (
np.floor(((x.size - cqtkernel.FFTLen) /
cqtkernel.fftHOP) + 1)
)
self.nframes += [nframes,]
self.cellCQT[i] = np.zeros([self.cqtkernel.bins *
self.cqtkernel.winNr,
nframes],
dtype=np.complex)
for n in np.arange(nframes):
if self.verbose>1:
print " nframe", n, "out of", nframes
# %applying fft to each column (each FFT frame)
framestart = n*cqtkernel.fftHOP
framestop = framestart + cqtkernel.FFTLen
X = np.fft.fft(x[framestart:framestop],
n=cqtkernel.FFTLen)
# %calculating cqt coefficients for all FFT frames
# for this octave
self.cellCQT[i][:,n] = np.dot(K, X)
if i != self.octaveNr:
x = spsig.filtfilt(self.B,self.A,x,) # %antialiasing filter
x = x[::2]# %drop samplerate by 2
else: # ... this loop seems overly highly unoptimized!
atomNr = self.cqtkernel.winNr
# number of hops that are not computed at the beginning of the
# data (beginning the first "window" starts at first_center,
# and not at 0):
emptyHops = self.cqtkernel.first_center *1./self.cqtkernel.atomHOP
ahop = self.cqtkernel.atomHOP # in samples, for the first octave
for i in np.arange(self.octaveNr):
if self.verbose:
print " Octave n.", i+1, "out of", self.octaveNr
inc = ahop / (2.**i)
# bin frequency numbers in the CQT transfo:
binVec = np.int32(
self.cqtkernel.bins * (self.octaveNr-i-1) +
np.arange(self.cqtkernel.bins)
)
# the number of frames in total to skip at the beginning of the
# CQT because of downsampling at each octave (so the higher
# the octave number, the less to skip)
# and since we want all the windows to be aligned,
# in order to have the first window of the lowest frequency
# octave to be centered with the first "effective" window in
# high freqs, we need to add as many 0s as follows, for each
# octave:
drop = emptyHops * (2**(self.octaveNr-i-1) - 1)
# There are too many frames computed at each octave, and we
# have to drop them so as to obtain aligned frames at all
# octaves.
if self.verbose>2:
print " drop", drop # DEBUG
nframes = (
np.floor(((x.size - cqtkernel.FFTLen)/cqtkernel.fftHOP)+1)
)
self.nframes += [nframes,]
if i==0:
self._spCQT = np.zeros([self.cqtkernel.bins
* self.octaveNr,
nframes * atomNr],
dtype=np.complex)
CQTframe = np.zeros([self.cqtkernel.bins * atomNr, nframes],
dtype=np.complex)
XX = np.zeros([self.cqtkernel.FFTLen, nframes],
dtype=np.complex)
nshifts = int(2**i)
for n in np.arange(nframes):
if self.verbose>2:
print " nframe", n+1, "out of", nframes
# %applying fft to each column (each FFT frame)
framestart = n * self.cqtkernel.fftHOP
framestop = framestart + self.cqtkernel.FFTLen
##X = np.fft.fft(x[framestart:framestop],
## n=cqtkernel.FFTLen)
XX[:,n]= np.fft.fft(x[framestart:framestop],
n=cqtkernel.FFTLen)
for nshift in np.arange(2**i):
# each shift in this loop corresponds to a different
# "starting point", aiming at compensating the arbitrary
# time stamps resulting from the subsampling, before
# each octave.
#
if self.verbose>1:
print " shift n.", nshift+1, "out of", (2**i)
shift = nshift * inc
# in original matlab code, this is conjugated,
# but K is here already conjugated
phShiftVec = np.exp(1j * 2 * np.pi *
np.arange(self.cqtkernel.FFTLen) *
shift / self.cqtkernel.FFTLen)
Kshift = K * phShiftVec
# %calculating cqt coefficients for all FFT frames
# for this octave
CQTframe = np.dot(Kshift, XX, out=CQTframe)
if nshift==0:
self.cellCQT[i] = np.copy(CQTframe)
if atomNr>1:
if self.verbose>1:
print " filling in CQT matrix, "+\
"many windows per frame"
for nb, b in enumerate(binVec):
# nb is the bin number in the current octave
# representation
# b is the bin number in the final CQT
# representation
for a in range(int(atomNr)):
# a is the number of the "sub-window"
# as computed by the above matrix product
# Kshift * XX
self._spCQT[
b,
int(nshift+a*nshifts):\
int((nframes)*atomNr*nshifts):\
int(atomNr*nshifts)] = (
CQTframe[nb*atomNr+a]
)
# doing the following shift only after all
# shifts have been computed, to realign the time
# series
##self._spCQT[b, :(self._spCQT.shape[1]
## -int(drop*nshifts))] = (
## self._spCQT[b, int(drop*nshifts):]
## )
else:
# TODO: not sure whether this piece of code works:
# needs testing!
if self.verbose>1:
print " filling in CQT matrix, "+\
"one window per frame"
self._spCQT[binVec,
int(nshift):(nframes*nshifts):nshifts] = (
CQTframe[:,:] #CQTframe[:,int(drop*nshifts):]
)
# aligning the time series, with the right drop:
for nb, b in enumerate(binVec):
self._spCQT[b,:(self._spCQT.shape[1]-int(drop*nshifts))]=(
self._spCQT[b, int(drop*nshifts):]
)
if i != self.octaveNr-1:
x = spsig.filtfilt(self.B,self.A,x) # %anti aliasing filter
x = x[::2]# %drop samplerate by 2
def _set_transfo(self, X):
"""for now, we return the spCQT: matrix form of the transform
"""
Xshape = X.shape
# spShape = self._spCQT.shape
if Xshape[0] == self.freqbins: # spShape:
self._spCQT = np.copy(X)
self.spCQT2CellCQT()
else:
raise ValueError('Transfo not of the right size: '+str(X.shape)+
' instead of '+str(self.freqbins))
def _get_transfo(self):
"""for now, we return the spCQT: matrix form of the transform
"""
return self._get_spCQT()
def _del_transfo(self):
"""erasing everything
"""
del self._spCQT
del self.cellCQT
transfo = property(fget=_get_transfo,
fdel=_del_transfo,
fset=_set_transfo,
doc="returns the computed transform")
def _get_time_stamps(self):
"""getting the time stamps for spCQT,
which also includes the prefix zeros that were pre-pended to the data.
(in samples)
"""
nframes = self.nframes[0] * self.cqtkernel.winNr # self.spCQT.shape[1]
time_stamps = (
np.arange(nframes) * self.cqtkernel.atomHOP
+ self.cqtkernel.first_center *
2**(self.octaveNr-1)
- self.prefixZeros
)
return time_stamps
time_stamps = property(fget=_get_time_stamps,
doc="time stamps for spCQT")
def _get_frequencies(self):
return self._compute_frequencies()
freq_stamps = property(fget=_get_frequencies,
doc="frequency stamps for spCQT")
def _compute_frequencies(self):
freqs = (
self.cqtkernel.fmin
* 2 ** (np.arange(self.cqtkernel.bins *
self.octaveNr) / self.cqtkernel.bins
- (self.octaveNr-1))
)
return freqs
def _get_qvalues(self):
Q = (
self.freq_stamps[:-1] /
np.diff(self.freq_stamps))
return Q
qValues = property(fget= _get_qvalues,
doc="$Q$ values, approximated")
# make spCQT a property: if not using rasterized version, then this matrix
# needs to be explicitly computed (contrary to cellCQT)
#
# _get_spCQT does therefore not need to be called explicitly (but could)
def _get_spCQT(self):
if not hasattr(self, '_spCQT') and hasattr(self, 'cellCQT'):
# compute the full cqt from self.cellCQT
self.cellCQT2spCQT()
elif not hasattr(self, '_spCQT') and not hasattr(self, 'cellCQT'):
raise AttributeError("Some CQT should be computed before"+\
" getting it.")
return self._spCQT
def _set_spCQT(self, value):
self._spCQT = value
if self.verbose>1:
print " Setting spCQT, resetting cellCQT"
self.spCQT2CellCQT()
spCQT = property(fget=_get_spCQT,
fset=_set_spCQT,
doc=("spCQT: the constant Q transform, "+
"in a readable format."))
def _get_cellCQT(self):
self.checkCQTexists()
if hasattr(self, 'cellCQT'):
return self.cellCQT
# now, if not, according to check CQTexists, self._spCQT exists.
self.spCQT2CellCQT()
return self.cellCQT
def plotCellCQT(self):
if not hasattr(self, 'cellCQT'):
raise AttributeError("This CQT instance has no cellCQT. "+
"\nUse computeCQT for this.")
import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure()
valueLims = [0,-1e10]
for i in np.arange(self.octaveNr):
cqt = self.cellCQT[i]
ax = fig.add_subplot(
self.octaveNr, 1, i+1)
ax.imshow(np.log(np.abs(cqt)),
interpolation='nearest',
origin='lower')
if np.log(np.abs(cqt)).max()>valueLims[1]:
valueLims[1] = np.log(np.abs(cqt)).max()
valueLims[0] = valueLims[1] - 10
for ax in fig.get_axes():
ax.get_images()[0].set_clim(valueLims)
def checkCQTexists(self):
if not hasattr(self, '_spCQT') and not hasattr(self, 'cellCQT'):
raise AttributeError("Compute the CQT with computeCQT before"+\
"requesting any of its representation")
return True
[docs] def invertFromSpCQT(self):
"""Assuming we have self.spCQT, and not self.cellCQT, we recompute
self.cellCQT from self.spCQT, and then invert as usual.
NB: here, self.cellCQT is written over, if it existed.
"""
if self.verbose:
print "Computing the cell representation from the matrix "+\
"representation..."
self.spCQT2CellCQT()
if self.verbose:
print "... and then computing the inverse from the newly "+\
"generated cells."
return self.invertFromCellCQT()
[docs] def invertFromSpCQTRast(self):
"""this inverts the transform, if perfRast, then this means
we can invert each hop of the different octaves.
"""
self._check_attr_inversion()
# inverting Kernel:
K = np.ascontiguousarray(self.cqtkernel.sparKernel)
y = np.zeros(np.ceil(self.datalen_init /
(2.**(self.octaveNr-1))))
atomNr = self.cqtkernel.winNr
emptyHops = self.cqtkernel.first_center * 1. / self.cqtkernel.atomHOP
ahop = self.cqtkernel.atomHOP # in samples, for the first octave
tmpSpCQT = np.copy(self._spCQT) # copying so we can modify it later
# from low to high octave:
for noct in range(int(self.octaveNr-1), -1, -1):
if self.verbose:
print " Octave n.", noct+1, "out of", self.octaveNr
inc = ahop / (2.**noct)
nshifts = int(2**noct)
nframes = int(self.nframes[noct])
ylen = (
self.cqtkernel.fftHOP * (nframes-1)
+ self.cqtkernel.FFTLen
+ nshifts * inc # adding the different shifts
)
if ylen > y.size:
y = np.concatenate([y, np.zeros(ylen-y.size)])
for nshift in np.arange(nshifts):
if self.verbose>1:
print " shift n.", nshift+1, "out of", nshifts
self.spCQT2CellCQT()
Y = np.dot(K, self.cellCQT[noct])
yoct = np.zeros(self.cqtkernel.FFTLen)
for n in range(nframes):
frastart = int(n * self.cqtkernel.fftHOP + nshift * inc)
frastop = frastart + self.cqtkernel.FFTLen
# divide by nshifts because we know there are as many
# redundant shifts to overlap and add.
yoct = (
np.fft.ifft(Y[:,n], n=self.cqtkernel.FFTLen)
/ np.double(nshifts))
yoct = 2. * np.real(yoct)
y[frastart:frastop] += yoct
# shift the spCQT for that octave by one to the left,
# we end up with the windows which are at inc sample
# (for this decimated octave version of y):
self._spCQT[int(self.cqtkernel.bins*(self.octaveNr-noct-1)):
int(self.cqtkernel.bins*(self.octaveNr-noct)),
:-1] = (
self._spCQT[int(self.cqtkernel.bins*(self.octaveNr-noct-1)):
int(self.cqtkernel.bins*(self.octaveNr-noct)),
1:]
)
# upsampling the signal by factor 2:
if noct != 0:
newy = np.zeros(y.size*2)
newy[::2] = y
y = spsig.filtfilt(self.B,
self.A,
newy)
del newy
y *= 2
self._spCQT = np.copy(tmpSpCQT)
del tmpSpCQT
y = y[self.prefixZeros:]
y = y[:self.datalen_init]
return y
[docs] def cellCQT2spCQT(self):
"""compute the full cqt from self.cellCQT"""
# compute the full cqt from self.cellCQT
# nb of frames for spCQT, as in cask.
spCQTframes = self.nframes[0] * self.cqtkernel.winNr
self._spCQT = np.zeros([self.cqtkernel.bins *
self.octaveNr,
self.cellCQT[0].shape[1] *
self.cqtkernel.winNr],
# self.nframes[0] * self.cqtkernel.winNr],
dtype=np.complex)
nfreqbins = self.cqtkernel.bins * self.octaveNr
#for fn in range(nfreqbins):
#
atomNr = self.cqtkernel.winNr
emptyHops = self.cqtkernel.first_center *1./self.cqtkernel.atomHOP
ahop = self.cqtkernel.atomHOP
for noct in range(int(self.octaveNr)):
drop = emptyHops * (2**(self.octaveNr-noct-1)) - emptyHops
#drop = 0 # DEBUG
binVec = np.int32(
self.cqtkernel.bins * (self.octaveNr-noct-1) +
np.arange(self.cqtkernel.bins)
)
nshifts = int(2**noct)
# self.cellCQT
if atomNr>1:
if self.verbose>1:
print " filling in CQT matrix, "+\
"many windows per frame"
for nb, b in enumerate(binVec):
for a in range(int(atomNr)):
self._spCQT[
b,
int(a*nshifts):\
int((self.cellCQT[noct].shape[1]) *
atomNr * nshifts):\
int(atomNr * nshifts)] = (
self.cellCQT[noct][nb*atomNr+a]
)
self._spCQT[b,:(self._spCQT.shape[1]
-int(drop*nshifts))] = (
self._spCQT[b, int(drop*nshifts):]
)
else:
if self.verbose>1:
print " filling in CQT matrix, "+\
"one window per frame"
##print self._spCQT[binVec, :int((self.cellCQT[noct].shape[1]
## -drop) * nshifts):nshifts].shape#DEBUG
##print self.cellCQT[noct][:, int(drop):].shape#DEBUG
self._spCQT[binVec, :int((self.cellCQT[noct].shape[1]
-drop) * nshifts):nshifts] = (
# might raise a bound error here...
self.cellCQT[noct][:, int(drop):]
)
# interpolating the values:
if noct>0:
for nb, b in enumerate(binVec):
yint = np.abs(self._spCQT[b][::nshifts])
xint = np.arange(start=0,
stop=self._spCQT.shape[1],
step=nshifts)
yint = np.abs(self._spCQT[b][::nshifts])
finterp = (
spinterp.interp1d(x=xint,
y=yint,
kind='linear',
bounds_error=False,
fill_value=0)
)
oldspcqt = np.copy(self._spCQT[b, np.int32(xint)])
self._spCQT[b] = (
finterp(np.arange(self._spCQT.shape[1])))
self._spCQT[b, xint] = oldspcqt
# resizing spCQT to fit original size:
self._spCQT = (np.ascontiguousarray(self._spCQT[:,:spCQTframes]))
[docs] def spCQT2CellCQT(self):
""" generates self.cellCQT from self.spCQT
NB: after transformation of spCQT (by filtering, for instance),
this method only keeps downsampled versions of each CQT representation
for each octave. More elaborated computations may be necessary to
take into account more precise time variations at low frequency
octaves.
"""
if not hasattr(self, 'spCQT'):
raise AttributeError("There is no spCQT field in this object. "+\
"Compute the transform or fill it in from "+\
"another object before this computation!")
self.cellCQT = {}
# now, if not, according to check CQTexists, self._spCQT exists.
emptyHops = self.cqtkernel.first_center *1./self.cqtkernel.atomHOP
self.cellCQT = {}
for noct in range(int(self.octaveNr)):
dropped = emptyHops * (2.**(self.octaveNr-noct-1) - 1)
X = self._spCQT[int(self.cqtkernel.bins*(self.octaveNr-noct-1)):
int(self.cqtkernel.bins*(self.octaveNr-noct)),
::int(2**noct)]
X = (
np.hstack([np.zeros([self.cqtkernel.bins, dropped]),
X,])
)
X = (
np.hstack([X,
np.zeros([self.cqtkernel.bins,
np.ceil(X.shape[1]/
self.cqtkernel.winNr)*
self.cqtkernel.winNr -
X.shape[1]])])
)
##print X.shape #DEBUG
if self.cqtkernel.winNr>1:
# in order to keep the same size as the original,
# we set the cell shapes as done in computeCQT instead:
##nframes = (
## np.floor(
## ((self.datalen_init + self.prefixZeros + self.suffixZeros
## - self.cqtkernel.FFTLen) / self.cqtkernel.fftHOP) + 1))
self.cellCQT[noct] = np.zeros([self.cqtkernel.bins *
self.cqtkernel.winNr,
#nframes],
np.ceil(X.shape[1]/
self.cqtkernel.winNr)],
dtype=np.complex)
for nbin in range(int(self.cqtkernel.bins)):
##print int(nbin*self.cqtkernel.winNr) #DEBUG
##print int((nbin+1)*self.cqtkernel.winNr) #DEBUG
self.cellCQT[noct][int(nbin*self.cqtkernel.winNr):\
int((nbin+1)*self.cqtkernel.winNr),
:] = (
X[nbin].reshape(
self.cqtkernel.winNr,
X.shape[1]/self.cqtkernel.winNr, order='F')
)
else:
self.cellCQT[noct] = np.copy(X)
self.cellCQT[noct] = (
np.ascontiguousarray(self.cellCQT[noct][:,:self.nframes[noct]])
)
[docs] def invertFromCellCQT(self):
"""inverting the Cell CQT
"""
self._check_attr_inversion()
# inverting Kernel:
K = np.ascontiguousarray(self.cqtkernel.sparKernel)
y = np.zeros(np.ceil(self.datalen_init /
(2.**(self.octaveNr-1))))
for noct in range(int(self.octaveNr-1), -1, -1):
Y = np.dot(K, self.cellCQT[noct])
_, nframes = self.cellCQT[noct].shape
ylen = (
self.cqtkernel.fftHOP * (nframes-1)
+ self.cqtkernel.FFTLen
)
if ylen > y.size:
y = np.concatenate([y, np.zeros(ylen-y.size)])
yoct = np.zeros(self.cqtkernel.FFTLen)
for n in range(nframes):
frastart = n * self.cqtkernel.fftHOP
frastop = frastart + self.cqtkernel.FFTLen
yoct = np.fft.ifft(Y[:,n], n=self.cqtkernel.FFTLen)
yoct = 2. * np.real(yoct)
y[frastart:frastop] += yoct
if noct != 0:
newy = np.zeros(y.size*2)
newy[::2] = y
y = spsig.filtfilt(self.B,
self.A,
newy)
del newy
y *= 2
y = y[self.prefixZeros:]
y = y[:self.datalen_init]
return y
def _check_attr_inversion(self):
"""a function that raises an error if any of the needed
attributes are not there, before trying to invert the transform
"""
necessaryAttributes = ['datalen_init',
'prefixZeros',
'suffixZeros',
'octaveNr']
for attr in necessaryAttributes:
if not hasattr(self, attr):
raise AttributeError(
"Missing attribute to compute the inverse "+\
"transform: %s." %attr)
return True
[docs]class HybridCQTransfo(CQTransfo):
"""Hybrid Constant Q Transform"""
def __init__(self, **kwargs):
super(HybridCQTransfo, self).__init__(**kwargs)
# but the cqtkernel is augmented as an hybrid kernel:
self.cqtkernel = HybridCQTKernel(
fmax=self.fmax,
bins=self.bins,
fs=self.fs,
q=self.q,
atomHopFactor=self.atomHopFactor,
thresh=self.thresh,
winFunc=self.winFunc,
perfRast=self.perfRast)
self.freqbins = self.bins*self.octaveNr + self.cqtkernel.linBins
if 'data' in kwargs.keys() and kwargs['data'] is not None:
self.computeLinearPart(data=kwargs['data'])
[docs] def cellCQT2spCQT(self):
"""the spCQT is computed from self.cellCQT
"""
# this computes the cellCQT for the octaves of the CQT:
# NB: super... returns self._spCQT
# maybe more efficient would be to have a
# separate method that computes and the other
# one that returns it? j'ignore de le savoir...
super(HybridCQTransfo,self).cellCQT2spCQT()
# now compute for linear frequency part:
# alignment should be the same as for octave=0
self._spCQT = np.concatenate([
self._spCQT,
np.zeros([self.cqtkernel.linBins,
self._spCQT.shape[1]],
dtype=np.complex)
])
atomNr = self.cqtkernel.winNr
emptyHops = self.cqtkernel.first_center *1./self.cqtkernel.atomHOP
ahop = self.cqtkernel.atomHOP
drop = emptyHops * (2**(self.octaveNr-1)) - emptyHops
binVec = np.int32(
self.cqtkernel.bins * self.octaveNr +
np.arange(self.cqtkernel.linBins)
)
if atomNr>1:
if self.verbose>1:
print " filling in CQT matrix, "+\
"many windows per frame"
for nb, b in enumerate(binVec):
for a in range(int(atomNr)):
self._spCQT[
b,
int(a):\
int(self.cellCQT['linear'].shape[1] *
atomNr):\
int(atomNr)] = (
self.cellCQT['linear'][nb*atomNr+a]
)
self._spCQT[b,:(self._spCQT.shape[1]
-int(drop))] = (
self._spCQT[b, int(drop):]
)
else:
if self.verbose>1:
print " filling in CQT matrix, "+\
"one window per frame"
self._spCQT[binVec, :int(self.cellCQT['linear'].shape[1]
-drop)] = (
# might raise a bound error here...
self.cellCQT['linear'][:,int(drop):]
)
[docs] def computeHybrid(self, data):
"""calculates a hybrid CQT/FT representation of a sound stored in data
"""
self.computeCQT(data=data)
# compute linear freqs part:
self.computeLinearPart(data=data)
[docs] def computeLinearPart(self, data):
"""Same as computeCQT, except it uses the linear frequency components
in cqtkernel.linearSparKernel
NB: since this should be equivalent to computing an FFT after windowing
each frame, there may be a faster way of implementing this function.
For now, keeping the same rules as the original CQT implementation,
for consistency and also for avoiding problems with window synchrony
"""
if self.verbose:
print "Computing ``missing'' linear frequency part of the spectrum"
cqtkernel = self.cqtkernel
# %zeropadding
x = (
np.concatenate([np.zeros(self.prefixZeros),
data,
np.zeros(self.suffixZeros)])
)
OVRLP = cqtkernel.FFTLen - cqtkernel.fftHOP
# %conjugate spectral kernel for cqt transformation
K = np.ascontiguousarray(np.conjugate(cqtkernel.linearSparKernel.T))
if not self.cqtkernel.perfRast:
if self.verbose:
print " Octave n.", 0, "out of", self.octaveNr
nframes = self.nframes[0]
self.cellCQT['linear'] = np.zeros([cqtkernel.linBins
* cqtkernel.winNr,
nframes],
dtype=np.complex)
# in theory we could avoid a loop, at the cost of
# more memory consumption...
XX = np.zeros([self.cqtkernel.FFTLen, nframes],
dtype=np.complex)
for n in np.arange(nframes):
if self.verbose>1:
print " nframe", n, "out of", nframes
framestart = n*cqtkernel.fftHOP
framestop = framestart + cqtkernel.FFTLen
XX[:,n] = np.fft.fft(x[framestart:framestop],
n=cqtkernel.FFTLen)
# %calculating cqt coefficients for all FFT frames
# for this octave
self.cellCQT['linear'] = np.dot(K, XX, out=self.cellCQT['linear'])
else:
atomNr = self.cqtkernel.winNr
emptyHops = self.cqtkernel.first_center *1./self.cqtkernel.atomHOP
ahop = self.cqtkernel.atomHOP
if self.verbose:
print " Octave n.", 0, "out of", self.octaveNr
binVec = np.int32(
self.cqtkernel.bins * self.octaveNr +
np.arange(self.cqtkernel.linBins)
)
# self._spCQT gets big...
self._spCQT = np.vstack([
self._spCQT,
np.zeros([self.cqtkernel.linBins,
self._spCQT.shape[1]],
dtype=np.complex)])
drop = emptyHops * (2**(self.octaveNr-1) - 1) # same as octave=0
nframes = self.nframes[0]
self.cellCQT['linear'] = np.zeros([self.cqtkernel.linBins * atomNr,
nframes],
dtype=np.complex)
XX = np.zeros([self.cqtkernel.FFTLen, nframes],
dtype=np.complex)
for n in np.arange(nframes):
if self.verbose>2:
print " nframe", n+1, "out of", nframes
# %applying fft to each column (each FFT frame)
framestart = n * self.cqtkernel.fftHOP
framestop = framestart + self.cqtkernel.FFTLen
##X = np.fft.fft(x[framestart:framestop],
## n=cqtkernel.FFTLen)
XX[:,n]= np.fft.fft(x[framestart:framestop],
n=cqtkernel.FFTLen)
self.cellCQT['linear'] = np.dot(K, XX, out=self.cellCQT['linear'])
if atomNr>1:
if self.verbose>1:
print " filling in CQT matrix, "+\
"many windows per frame"
for nb, b in enumerate(binVec):
for a in range(int(atomNr)):
self._spCQT[
b,
int(a):\
int((nframes)*atomNr):\
int(atomNr)] = (
self.cellCQT['linear'][nb*atomNr+a]
)
else:
# TODO: not sure whether this piece of code works:
# needs testing!
if self.verbose>1:
print " filling in CQT matrix, "+\
"one window per frame"
self._spCQT[binVec] = (
self.cellCQT['linear'] #CQTframe[:,int(drop*nshifts):]
)
for nb, b in enumerate(binVec):
self._spCQT[b,:(self._spCQT.shape[1]-int(drop))]=(
self._spCQT[b, int(drop):]
)
[docs] def invertHybridCQT(self):
"""Invert the hybrid transform.
Linearity allows to perform the cqt inverse first, and add the inverse
of the linear freqs part thereafter (or the other way around).
"""
y = self.invertFromCellCQT()
y += self.invertLinearPart()
return y
[docs] def invertLinearPart(self):
"""This inverts the linear part of the hybrid transform
NB: as for the computation of this part in transform, a windowed
version
of a plain FFT should do the same job, and faster.
"""
K = np.ascontiguousarray(self.cqtkernel.linearSparKernel)
y = np.zeros(np.ceil(
self.prefixZeros +
self.datalen_init +
self.suffixZeros))
Y = np.dot(K, self.cellCQT['linear'])
_, nframes = Y.shape
ylen = (
self.cqtkernel.fftHOP * (nframes-1)
+ self.cqtkernel.FFTLen
)
if ylen > y.size:
y = np.concatenate([y, np.zeros(ylen-y.size)])
yoct = np.zeros(self.cqtkernel.FFTLen)
for n in range(nframes):
frastart = n * self.cqtkernel.fftHOP
frastop = frastart + self.cqtkernel.FFTLen
yoct = np.fft.ifft(Y[:,n])
yoct = 2. * np.real(yoct)
y[frastart:frastop] += yoct
y = y[self.prefixZeros:]
y = y[:self.datalen_init]
return y
def spCQT2CellCQT(self):
# populating the CQT part of cellCQT
super(HybridCQTransfo, self).spCQT2CellCQT()
# now computing the linear frequency part
emptyHops = self.cqtkernel.first_center * 1. / self.cqtkernel.atomHOP
dropped = emptyHops * (2.**(self.octaveNr - 1) - 1)
X = self._spCQT[int(self.cqtkernel.bins * self.octaveNr):
int(self.cqtkernel.bins * self.octaveNr
+ self.cqtkernel.linBins)]
X = (
np.hstack([np.zeros([self.cqtkernel.linBins, dropped]),
X,])
)
X = (
np.hstack([X,
np.zeros([self.cqtkernel.linBins,
np.ceil(X.shape[1] /
self.cqtkernel.winNr)*
self.cqtkernel.winNr -
X.shape[1]])])
)
if self.cqtkernel.winNr>1:
self.cellCQT['linear'] = np.zeros([self.cqtkernel.linBins *
self.cqtkernel.winNr,
np.ceil(X.shape[1]/
self.cqtkernel.winNr)],
dtype=np.complex)
for nbin in range(int(self.cqtkernel.linBins)):
##print int(nbin*self.cqtkernel.winNr) #DEBUG
##print int((nbin+1)*self.cqtkernel.winNr) #DEBUG
self.cellCQT['linear'][int(nbin*self.cqtkernel.winNr):\
int((nbin+1)*self.cqtkernel.winNr)] = (
X[nbin].reshape(
self.cqtkernel.winNr,
X.shape[1]/self.cqtkernel.winNr, order='F')
)
else:
self.cellCQT['linear'] = np.copy(X)
# resizing to fit original size:
self.cellCQT['linear'] = (
np.ascontiguousarray(self.cellCQT['linear'][:,:self.nframes[0]])
)
def _compute_frequencies(self):
freqs = super(HybridCQTransfo, self)._compute_frequencies()
linfreqs = (
np.arange(self.cqtkernel.linBins, dtype=np.float64) *
self.cqtkernel.fs /
self.cqtkernel.Nk_max# self.cqtkernel.FFTLen
+ self.fmax * 2 ** (1. / self.cqtkernel.bins)
)
return np.concatenate([freqs, linfreqs])
[docs]class MinQTransfo(CQTransfo):
"""Minimum Q Transform"""
transformname = 'minqt'
def __init__(self, fmax, bins, linFTLen, fs, fmin=70,
**kwargs):
"""MinQTransform : MinQT
"""
# not computing the cqtkernel there, hence a "dummy" one:
super(MinQTransfo, self).__init__(fmax=fmax, fs=fs,
bins=bins,cqtkernel=0,
fmin=fmin,
**kwargs)
# but the cqtkernel is augmented as the MinQT kernel:
self.cqtkernel = MinQTKernel(
linFTLen=linFTLen,
fmax=fmax,
bins=bins,
fs=fs,
q=self.q,
atomHopFactor=self.atomHopFactor,
thresh=self.thresh,
winFunc=self.winFunc,
perfRast=self.perfRast)
self.octaveNr = np.ceil(np.log2(self.cqtkernel.fmax * 1. / fmin))
self.fmin = (self.cqtkernel.fmax / (2.**self.octaveNr)) * 2**(1./bins)
self.freqbins = (
self.octaveNr * self.cqtkernel.bins
+ self.cqtkernel.linBins
)
if 'data' in kwargs.keys() and kwargs['data'] is not None:
self.computeLinearPart(data=kwargs['data'])
[docs] def computeLinearPart(self, data):
"""Compute the linear frequency part with an STFT, and
taking only the desired frequencies.
"""
if self.verbose:
print "Computing ``missing'' linear frequency part of the spectrum"
# to get the matrices aligned, needs to have the first frame
# of STFT centered on self.cqtkernel.first_center (?)
# %zeropadding
x = (
np.concatenate([np.zeros(self.prefixZeros),
data,
np.zeros(self.suffixZeros)])
)
# since qo.stft centers the first frame on the first
# sample of the data, we need to remove half a window:
self.offsetSTFT = (self.cqtkernel.first_center)
X,F,N = stft(data=x[self.offsetSTFT:],
window=self.cqtkernel.linWindow,
hopsize=self.cqtkernel.atomHOP,
nfft=self.cqtkernel.linFTLen,
fs=self.cqtkernel.fs)
# filling cellCQT, assuming same number of frames:
self.cellCQT['linear'] = X[self.cqtkernel.Kmax:,
:self.nframes[0] * self.cqtkernel.winNr]
if self.perfRast:
# self._spCQT gets big...
self.linCellCQT2LinSpCQT()
##self._spCQT = np.vstack([
## self._spCQT,
## np.zeros([self.cqtkernel.linBins,
## self._spCQT.shape[1]],
## dtype=np.complex)])
##emptyHops = self.cqtkernel.first_center *1./self.cqtkernel.atomHOP
##drop = emptyHops * (2**(self.octaveNr-1) - 1) # same as octave=0
### filling _spCQT:
##self._spCQT[(self.cqtkernel.bins * self.octaveNr):,
## :self._spCQT.shape[1]-int(drop)] = (
## self.cellCQT['linear'][:,int(drop):]
## )
[docs] def invertFromSpCQTRast(self):
"""
"""
y = super(MinQTransfo, self).invertFromSpCQTRast()
y += self.invertLinearPart()
return y
[docs] def invertLinearPart(self):
"""This inverts the linear part of the hybrid transform
NB: as for the computation of this part in transform, a windowed
version
of a plain FFT should do the same job, and faster.
"""
Y = np.zeros([self.cqtkernel.linFTLen / 2 + 1,
self.cellCQT['linear'].shape[1]],
dtype=np.complex)
Y[self.cqtkernel.Kmax:] = self.cellCQT['linear']
y = istft(X=Y, window=self.cqtkernel.linWindow,
hopsize=self.cqtkernel.atomHOP,
nfft=self.cqtkernel.linFTLen)
y = y[(self.prefixZeros-self.offsetSTFT):]
y = y[:self.datalen_init]
return y
def _compute_frequencies(self):
freqs = super(MinQTransfo, self)._compute_frequencies()
linfreqs = (
np.arange(
self.cqtkernel.Kmax,
self.cqtkernel.Kmax + self.cqtkernel.linBins,
dtype=np.float64) *
self.cqtkernel.fs /
self.cqtkernel.linFTLen # self.cqtkernel.FFTLen
# + self.fmax * 2 ** (1. / self.cqtkernel.bins)
)
return np.concatenate([freqs, linfreqs])
[docs] def spCQT2CellCQT(self):
"""Reverting spCQT (matrix form of the transform) back into
the (original?) cellCQT (one matrix per octave, one for the linear
part of the transform).
"""
# populating the CQT part of cellCQT
super(MinQTransfo, self).spCQT2CellCQT()
# now computing the linear frequency part:
emptyHops = self.cqtkernel.first_center * 1. / self.cqtkernel.atomHOP
dropped = emptyHops * (2.**(self.octaveNr - 1) - 1)
X = self._spCQT[int(self.cqtkernel.bins * self.octaveNr):
int(self.cqtkernel.bins * self.octaveNr
+ self.cqtkernel.linBins)]
X = (
np.hstack([np.zeros([self.cqtkernel.linBins, dropped]),
X,])
)
self.cellCQT['linear'] = np.copy(X)
# resizing to fit original size:
self.cellCQT['linear'] = (
np.ascontiguousarray(
self.cellCQT['linear'][:,:(self.nframes[0]*
self.cqtkernel.winNr)])
)
[docs] def cellCQT2spCQT(self):
"""converts the cellCQT into a spCQT (matrix form)
"""
# computing the self._spCQT from the CQT cellCQT cells:
super(MinQTransfo, self).cellCQT2spCQT()
self.linCellCQT2LinSpCQT()
[docs] def linCellCQT2LinSpCQT(self):
"""converts cellCQT['linear'] into the corresponding bins in
self._spCQT
"""
self._spCQT = np.vstack([
self._spCQT,
np.zeros([self.cqtkernel.linBins,
self._spCQT.shape[1]],
dtype=np.complex)])
emptyHops = self.cqtkernel.first_center * 1. / self.cqtkernel.atomHOP
drop = emptyHops * (2**(self.octaveNr-1) - 1) # same as octave=0
# filling _spCQT:
self._spCQT[(self.cqtkernel.bins * self.octaveNr):,
:self._spCQT.shape[1]-int(drop)] = (
self.cellCQT['linear'][:,int(drop):]
)