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>]
../_images/_notebooks_time_frequency_transform_length_constraint_4_1.png

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.)
../_images/_notebooks_time_frequency_transform_length_constraint_8_0.png
In [5]:
_ = stft_pad.apply(x).plot_spectrogram(dynrange=100.)
../_images/_notebooks_time_frequency_transform_length_constraint_9_0.png

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.)
../_images/_notebooks_time_frequency_transform_length_constraint_11_0.png

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>]
../_images/_notebooks_time_frequency_transform_length_constraint_14_1.png

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
../_images/_notebooks_time_frequency_transform_length_constraint_20_1.png

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
../_images/_notebooks_time_frequency_transform_length_constraint_22_1.png