Time-frequency transforms : constraints on the transform length¶
The examples below illustrate how to use the param_constraint
option
to fit the transform length constraints, i.e. the signal length must be
a multiple of the hop size and of the number of frequency bins.
In [1]:
%pylab inline
import numpy as np
from madarrays import Waveform
from pyteuf import Stft
Populating the interactive namespace from numpy and matplotlib
Case 1 : parameters fitting the constraints¶
Let us define a signal composed of a pure sine
In [2]:
signal_params = {'sig_len': 32, 'fs': 1}
f0 = signal_params['fs'] / 8
x = Waveform(np.sin(2 * np.pi * f0 * np.arange(signal_params['sig_len'])),
fs = signal_params['fs'])
x.plot()
Out[2]:
[<matplotlib.lines.Line2D at 0x7fcd8dc9e748>]
We use three similar Stft
that only differ in the
param_constraint
option.
In [3]:
stft_params = {'hop': 8, 'n_bins': 16, 'win_name': 'hann', 'win_len': 16, 'zero_pad_full_sig': False}
stft_fix = Stft(param_constraint='fix', **stft_params)
print(stft_fix)
stft_pad = Stft(param_constraint='pad', **stft_params)
print(stft_pad)
stft_pow2 = Stft(param_constraint='pow2', **stft_params)
print(stft_pow2)
***************************** STFT *****************************
Specified analysis window: hann, 16 samples
Tight: False
Hop length: 8 samples
16 frequency bins
Convention: lp
param_constraint: fix
zero_pad_full_sig: False
****************************************************************
***************************** STFT *****************************
Specified analysis window: hann, 16 samples
Tight: False
Hop length: 8 samples
16 frequency bins
Convention: lp
param_constraint: pad
zero_pad_full_sig: False
****************************************************************
***************************** STFT *****************************
Specified analysis window: hann, 16 samples
Tight: False
Hop length: 8 samples
16 frequency bins
Convention: lp
param_constraint: pow2
zero_pad_full_sig: False
****************************************************************
Since the constraint is satisfied, stft_fix
and stft_pad
transforms give the same results
In [4]:
_ = stft_fix.apply(x).plot_spectrogram(dynrange=100.)
In [5]:
_ = stft_pad.apply(x).plot_spectrogram(dynrange=100.)
Since the hop size and the number of bins are powers of 2, the results
by stft_pow2
is also the same.
In [6]:
_ = stft_pow2.apply(x).plot_spectrogram(dynrange=100.)
Case 2: parameters not fitting the constraints¶
Again, the signal is composed of a pure sine, but its size is different.
In [7]:
signal_params = {'sig_len': 50, 'fs': 1}
f0 = signal_params['fs'] / 8
x = Waveform(np.sin(2 * np.pi * f0 * np.arange(signal_params['sig_len'])),
fs = signal_params['fs'])
x.plot()
Out[7]:
[<matplotlib.lines.Line2D at 0x7fcd8da143c8>]
We use three similar Stft
that only differ in the
param_constraint
option and choose a hop size and a number of bins
that are not divisor of the signal length.
In [8]:
stft_params = {'hop': 9, 'n_bins': 17, 'win_name': 'hann', 'win_len': 16,
'zero_pad_full_sig':False}
stft_fix = Stft(param_constraint='fix', **stft_params)
print(stft_fix)
stft_pad = Stft(param_constraint='pad', **stft_params)
print(stft_pad)
stft_pow2 = Stft(param_constraint='pow2', **stft_params)
print(stft_pow2)
***************************** STFT *****************************
Specified analysis window: hann, 16 samples
Tight: False
Hop length: 9 samples
17 frequency bins
Convention: lp
param_constraint: fix
zero_pad_full_sig: False
****************************************************************
***************************** STFT *****************************
Specified analysis window: hann, 16 samples
Tight: False
Hop length: 9 samples
17 frequency bins
Convention: lp
param_constraint: pad
zero_pad_full_sig: False
****************************************************************
***************************** STFT *****************************
Specified analysis window: hann, 16 samples
Tight: False
Hop length: 8 samples
32 frequency bins
Convention: lp
param_constraint: pow2
zero_pad_full_sig: False
****************************************************************
For param_constraint='fix'
, an error is raised since the signal
length is not a multiple of the hop length.
In [9]:
try:
X_fix = stft_fix.apply(x)
except BaseException as e:
print('Error raised:', e)
Error raised: `hop` (9) is not a divisor of `sig_len` (50).
One option is to use param_constraint='pad'
: the signal is
zero-padded so that the transform-length constraint is satisfied. As
shown here, this may results in a adding a lot of zeros at the end of
the signal.
In [10]:
X_pad = stft_pad.apply(x)
print(X_pad)
_ = X_pad.plot_spectrogram(dynrange=100.)
========================
TF representation
------------------------
TF parameters
{'hop': 9, 'n_bins': 17, 'win_name': 'hann', 'win_len': 16, 'win_array': None, 'win_type': 'analysis', 'is_tight': False, 'convention': 'lp', 'param_constraint': 'pad', 'zero_pad_full_sig': False}
------------------------
Signals parameters
{'fs': 1, 'sig_len': 50}
------------------------
9 frequency bins
17 frames
Another option is to use param_constraint='pow2'
: the hop size and
the number of frequency bins are adjusted to a power of two and the
signal is zero-padded so that the transform-length constraint is
satisfied. Compared to the previous case, the transform parameters are
slightly adjusted to limit the padding length needed to satisfy the
constraint.
In [11]:
X_pow2 = stft_pow2.apply(x)
print(X_pow2)
_ = X_pow2.plot_spectrogram(dynrange=100.)
========================
TF representation
------------------------
TF parameters
{'hop': 8, 'n_bins': 32, 'win_name': 'hann', 'win_len': 16, 'win_array': None, 'win_type': 'analysis', 'is_tight': False, 'convention': 'lp', 'param_constraint': 'pow2', 'zero_pad_full_sig': False}
------------------------
Signals parameters
{'fs': 1, 'sig_len': 50}
------------------------
17 frequency bins
8 frames