In [1]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib

Time-Frequency analysis with missing data

Stft objects

Short-time Fourier transforms (STFT) of signals can be handled using Stft objects. This is a wrapper for the ltfatpy package.

Transform and inverse transform

Stft takes as input the parameters of the STFT, namely the hop size hop, the number of bins n_bins, the window type win_name and length win_len, as well as two other parameters, param_constraint (see tutorial on the constraints on the transform length) and zero_pad_full_sig (see tutorial on boundary effects).

In [2]:
from pyteuf import Stft
from madarrays import Waveform
In [3]:
stft = Stft(hop=16, n_bins=512, win_name='sine', win_len=256,
            param_constraint='pad', zero_pad_full_sig=False)
print(stft)
***************************** STFT *****************************
Specified analysis window: sine, 256 samples
Tight: False
Hop length: 16 samples
512 frequency bins
Convention: lp
param_constraint: pad
zero_pad_full_sig: False
****************************************************************

The inverse transform may obtained from the direct transform by

In [4]:
istft = stft.get_istft()
print(istft)
**************************** ISTFT *****************************
Specified analysis window: sine, 256 samples
Tight: False
Hop length: 16 samples
512 frequency bins
Convention: lp
****************************************************************

Example on a synthetic real signal

Create a signal composed of two sines

In [5]:
nu1 = 1/33
nu2 = 3/16
duration = 0.5
fs = 8000
t = np.arange(0, int(duration*fs)) / fs
x = np.cos(2*np.pi*t*nu1*fs) + 0.5*np.cos(2*np.pi*t*nu2*fs)

Compute STFT and display the spectrogram

In [6]:
X = stft.apply(x, fs=fs)
print(X)
_ = X.plot_spectrogram(dynrange=100.)
========================
TF representation
------------------------
TF parameters
{'hop': 16, 'n_bins': 512, 'win_name': 'sine', 'win_len': 256, 'win_array': None, 'win_type': 'analysis', 'is_tight': False, 'convention': 'lp', 'param_constraint': 'pad', 'zero_pad_full_sig': False}
------------------------
Signals parameters
{'fs': 8000, 'sig_len': 4000}
------------------------
257 frequency bins
256 frames
../_images/_notebooks_time_frequency_10_1.png

Note that the negative frequencies are not displayed since the STFT of a real signal is symetric hermitian, and that boundary effects appear at the beginning and the end of the time axis.

Reconstruction is obtained by:

In [7]:
y = istft.apply(X)
print('Reconstruction: {}'.format(y))
print('Reconstruction error: {:.3f} dB'.format(10 * np.log10(np.mean(np.abs(x - y)**2))))
Reconstruction: Waveform, fs=8000Hz, length=4000, dtype=float64, 0 missing entries (0.0%)
[ 1.5         1.17327041  0.57481454 ...,  0.26179427  0.22650352
  0.60675673]
Reconstruction error: -314.983 dB

Example on a synthetic complex signal

Create a waveform composed of two sines

In [8]:
nu1 = 1/33
nu2 = 3/16
duration = 0.5
fs = 8000
t = np.arange(0, int(duration*fs)) / fs
x = Waveform(np.exp(1j*2*np.pi*t*nu1*fs) + 0.5*np.exp(1j*2*np.pi*t*nu2*fs) , fs=fs)

print(x)
np.real(x).show_player()
Waveform, fs=8000Hz, length=4000, dtype=complex128, 0 missing entries (0.0%)
[ 1.50000000+0.j          1.17327041+0.65119101j  0.57481454+0.72521585j
 ...,  0.26179427+0.88142073j  0.22650352+0.46102256j
  0.60675673+0.44769223j]
Out[8]:
In [9]:
X = stft.apply(x)
print(X)
_ = X.plot_spectrogram(dynrange=100.)
========================
TF representation
------------------------
TF parameters
{'hop': 16, 'n_bins': 512, 'win_name': 'sine', 'win_len': 256, 'win_array': None, 'win_type': 'analysis', 'is_tight': False, 'convention': 'lp', 'param_constraint': 'pad', 'zero_pad_full_sig': False}
------------------------
Signals parameters
{'fs': 8000, 'sig_len': 4000}
------------------------
512 frequency bins
256 frames
../_images/_notebooks_time_frequency_16_1.png

Note that all the frequencies are shown since the signal is complex.

Reconstruction is obtained by:

In [10]:
y = istft.apply(X)
print('Reconstruction: {}'.format(y))
print('Reconstruction error: {:.3f} dB'.format(10 * np.log10(np.mean(np.abs(x - y)**2))))
Reconstruction: Waveform, fs=8000Hz, length=4000, dtype=complex128, 0 missing entries (0.0%)
[ 1.50000000 +2.72299507e-17j  1.17327041 +6.51191011e-01j
  0.57481454 +7.25215846e-01j ...,  0.26179427 +8.81420728e-01j
  0.22650352 +4.61022561e-01j  0.60675673 +4.47692229e-01j]
Reconstruction error: -312.610 dB

Example on a real sound

Load test sound

In [11]:
from ltfatpy import gspi
x, fs = gspi()
x = Waveform(x, fs=fs)

Note that one may also use static method Waveform.from_wavfile(filename) in order to load a Waveform object from an audio file.

Apply STFT and display properties of Stft data

In [12]:
X = stft.apply(x)
print(X)
========================
TF representation
------------------------
TF parameters
{'hop': 16, 'n_bins': 512, 'win_name': 'sine', 'win_len': 256, 'win_array': None, 'win_type': 'analysis', 'is_tight': False, 'convention': 'lp', 'param_constraint': 'pad', 'zero_pad_full_sig': False}
------------------------
Signals parameters
{'fs': 44100, 'sig_len': 262144}
------------------------
257 frequency bins
16384 frames

Get a related Istft object, apply istft, display properties of reconstruction

In [13]:
y = istft.apply(X)
print('Reconstruction: {}'.format(y))
print('Reconstruction error: {:.3f} dB'.format(10 * np.log10(np.mean(np.abs(x - y)**2))))
Reconstruction: Waveform, fs=44100Hz, length=262144, dtype=float64, 0 missing entries (0.0%)
[  4.49622788e-20  -2.42328347e-20   3.05175781e-05 ...,   9.46044922e-04
   2.74658203e-03   4.05883789e-03]
Reconstruction error: -333.369 dB

Note that x-y computes the difference between two Waveform object and returns a new Waveform, that may be displayed or processed, as below. Many operators can be applied in the same way to Waveform objects.

In [14]:
plt.figure(figsize=(12,2))
x.plot(y_axis_label=None)

plt.figure(figsize=(12,4))
X.plot_spectrogram(dynrange=100.)

plt.figure(figsize=(12,2))
(x-y).plot(y_axis_label=None)
plt.title('Error signal')
Out[14]:
Text(0.5,1,'Error signal')
../_images/_notebooks_time_frequency_28_1.png
../_images/_notebooks_time_frequency_28_2.png
../_images/_notebooks_time_frequency_28_3.png