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

TfData objects

A TfData is a MaskedArray dedicated to handle Time-Frequency representations obtained from a real signal. As such, it has two attributes tf_params and signal_params, giving respectively the parameters of the STFT used to obtain the representation, and the parameters of the real signali, as well as dedicated methods to facilitate the manipulation of time-frequency data.

In [2]:
from pyteuf import TfData, Stft
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['figure.figsize'] = (14, 4)

# Data parameters
tf_params = {'hop': 32, 'n_bins': 128, 'win_len': 64, 'win_name': 'hanning'}
signal_params = {'len':1024, 'fs':44100}

Example of TF representation of a complex signal, with binary masking

As for MaskedArray, TfData can be initialized from a 2D complex nd-array with or without mask. Parameters stft_params and signal_params are explicitly given if available. For a complex signal, coefficients for both negative and positive frequencies are handled and displayed

In [3]:
# Data ans mask for a complex signal (no symetry in TF)
tf_shape_complex = (tf_params['n_bins'], signal_params['len'] // tf_params['hop'])
mask_complex = np.zeros(tf_shape_complex)
mask_complex[int(0.2*tf_shape_complex[0]):int(0.6*tf_shape_complex[0]),
             int(0.25*tf_shape_complex[1]):int(0.7*tf_shape_complex[1])] = True
data_complex = np.random.randn(*tf_shape_complex) + 1j * np.random.randn(*tf_shape_complex)

# TF data
X = TfData(data=data_complex, mask=mask_complex, tf_params=tf_params, signal_params=signal_params)

plt.subplot(121)
X.plot_spectrogram()
plt.subplot(122)
X.plot_mask()
print(X)
========================
TF representation
------------------------
TF parameters
{'hop': 32, 'n_bins': 128, 'win_len': 64, 'win_name': 'hanning'}
------------------------
Signals parameters
{'len': 1024, 'fs': 44100}
------------------------
128 frequency bins
32 frames
../_images/_notebooks_tf_data_5_1.png

Example of TF representation of a real signal, with binary masking

For a real signal, only the non-negative frequencies area is handled due to Hermitian symetry.

In [4]:
# Data and mask for a real signal (Hermitian symetry in TF)
tf_shape_real = (tf_params['n_bins'] // 2 + 1, signal_params['len'] // tf_params['hop'])
mask_real = np.zeros(tf_shape_real)
mask_real[int(0.2*tf_shape_real[0]):int(0.6*tf_shape_real[0]),
          int(0.25*tf_shape_real[1]):int(0.7*tf_shape_real[1])] = True
data_real = np.random.randn(*tf_shape_real) + 1j * np.random.randn(*tf_shape_real)

# TF data
X = TfData(data=data_real, mask=mask_real, tf_params=tf_params, signal_params=signal_params)

plt.subplot(121)
X.plot_spectrogram()
plt.subplot(122)
X.plot_mask()
print(X)
========================
TF representation
------------------------
TF parameters
{'hop': 32, 'n_bins': 128, 'win_len': 64, 'win_name': 'hanning'}
------------------------
Signals parameters
{'len': 1024, 'fs': 44100}
------------------------
65 frequency bins
32 frames
../_images/_notebooks_tf_data_8_1.png

Example of TF representation with complex masking

With complex masking, two masks are handled: a mask for amplitudes and a mask for phases.

In [5]:
# Data and mask for a real signal (Hermitian symetry in TF)
tf_shape_real = (tf_params['n_bins'] // 2 + 1, signal_params['len'] // tf_params['hop'])
mask_mag = np.zeros(tf_shape_real)
mask_mag[int(0.2*tf_shape_real[0]):int(0.6*tf_shape_real[0]),
         int(0.25*tf_shape_real[1]):int(0.7*tf_shape_real[1])] = True
mask_phi = np.zeros(tf_shape_real)
mask_phi[int(0.5*tf_shape_real[0]):int(0.65*tf_shape_real[0]),
         int(0.65*tf_shape_real[1]):int(0.75*tf_shape_real[1])] = True
data_real = np.random.randn(*tf_shape_real) + 1j * np.random.randn(*tf_shape_real)

# TF data
X = TfData(data=data_real,
           mask_magnitude=mask_mag, mask_phase=mask_phi,
           tf_params=tf_params, signal_params=signal_params)
print(X)
========================
TF representation
------------------------
TF parameters
{'hop': 32, 'n_bins': 128, 'win_len': 64, 'win_name': 'hanning'}
------------------------
Signals parameters
{'len': 1024, 'fs': 44100}
------------------------
65 frequency bins
32 frames

One may select one of the masks or a combination of them for display.

In [6]:
for mask_type in ['any', 'all', 'magnitude', 'phase', 'magnitude only', 'phase only']:
    plt.figure()
    plt.subplot(121)
    X.plot_spectrogram(mask_type=mask_type)
    plt.title('mask type: {}'.format(mask_type))
    plt.subplot(122)
    X.plot_mask(mask_type=mask_type)
    plt.title('mask type: {}'.format(mask_type))

../_images/_notebooks_tf_data_13_0.png
../_images/_notebooks_tf_data_13_1.png
../_images/_notebooks_tf_data_13_2.png
../_images/_notebooks_tf_data_13_3.png
../_images/_notebooks_tf_data_13_4.png
../_images/_notebooks_tf_data_13_5.png

Selecting a mask type can also be used to extract the related mask:

In [7]:
for mask_type in ['any', 'all', 'magnitude', 'phase', 'magnitude only', 'phase only']:
    mask = X.get_unknown_mask(mask_type)
    print('Ratio of "{}" unknown coefficients: {:.1%}'.format(mask_type, np.mean(mask)))
    mask = X.get_known_mask(mask_type)
    print('Ratio of "{}" known coefficients: {:.1%}'.format(mask_type, np.mean(mask)))
Ratio of "any" unknown coefficients: 18.8%
Ratio of "any" known coefficients: 99.3%
Ratio of "all" unknown coefficients: 0.7%
Ratio of "all" known coefficients: 81.2%
Ratio of "magnitude" unknown coefficients: 17.5%
Ratio of "magnitude" known coefficients: 82.5%
Ratio of "phase" unknown coefficients: 1.9%
Ratio of "phase" known coefficients: 98.1%
Ratio of "magnitude only" unknown coefficients: 16.8%
Ratio of "magnitude only" known coefficients: 1.2%
Ratio of "phase only" unknown coefficients: 1.2%
Ratio of "phase only" known coefficients: 16.8%

Other useful properties:

In [8]:
print('Number of frequencies: {}'.format(X.n_frequencies))
print('Number of frames: {}'.format(X.n_frames))
print('Start time of frames: {}'.format(X.start_times))
print('Center time of frames: {}'.format(X.mid_times))
print('End time of frames: {}'.format(X.end_times))
Number of frequencies: 65
Number of frames: 32
Start time of frames: [ -7.02947846e-04   2.26757370e-05   7.48299320e-04   1.47392290e-03
   2.19954649e-03   2.92517007e-03   3.65079365e-03   4.37641723e-03
   5.10204082e-03   5.82766440e-03   6.55328798e-03   7.27891156e-03
   8.00453515e-03   8.73015873e-03   9.45578231e-03   1.01814059e-02
   1.09070295e-02   1.16326531e-02   1.23582766e-02   1.30839002e-02
   1.38095238e-02   1.45351474e-02   1.52607710e-02   1.59863946e-02
   1.67120181e-02   1.74376417e-02   1.81632653e-02   1.88888889e-02
   1.96145125e-02   2.03401361e-02   2.10657596e-02   2.17913832e-02]
Center time of frames: [ 0.          0.00072562  0.00145125  0.00217687  0.00290249  0.00362812
  0.00435374  0.00507937  0.00580499  0.00653061  0.00725624  0.00798186
  0.00870748  0.00943311  0.01015873  0.01088435  0.01160998  0.0123356
  0.01306122  0.01378685  0.01451247  0.0152381   0.01596372  0.01668934
  0.01741497  0.01814059  0.01886621  0.01959184  0.02031746  0.02104308
  0.02176871  0.02249433]
End time of frames: [ 0.00070295  0.00142857  0.0021542   0.00287982  0.00360544  0.00433107
  0.00505669  0.00578231  0.00650794  0.00723356  0.00795918  0.00868481
  0.00941043  0.01013605  0.01086168  0.0115873   0.01231293  0.01303855
  0.01376417  0.0144898   0.01521542  0.01594104  0.01666667  0.01739229
  0.01811791  0.01884354  0.01956916  0.02029478  0.02102041  0.02174603
  0.02247166  0.02319728]

Current operations between two TfData objects are not recommended since resulting operations on masks are not designed adequately:

In [9]:
mask1 = np.zeros(tf_shape_real)
mask1[int(0.2*tf_shape_real[0]):int(0.6*tf_shape_real[0]),
      int(0.25*tf_shape_real[1]):int(0.7*tf_shape_real[1])] = True
mask2 = np.zeros(tf_shape_real)
mask2[int(0.5*tf_shape_real[0]):int(0.65*tf_shape_real[0]),
      int(0.65*tf_shape_real[1]):int(0.75*tf_shape_real[1])] = True
data_real = np.random.randn(*tf_shape_real) + 1j * np.random.randn(*tf_shape_real)

# TF data
X1 = TfData(data=np.random.randn(*tf_shape_real) + 1j * np.random.randn(*tf_shape_real),
            mask=mask1, tf_params=tf_params, signal_params=signal_params)
X2 = TfData(data=np.random.randn(*tf_shape_real) + 1j * np.random.randn(*tf_shape_real),
            mask=mask2, tf_params=tf_params, signal_params=signal_params)

plt.figure()
plt.subplot(121)
X1.plot_spectrogram()
plt.subplot(122)
X2.plot_spectrogram()

plt.figure()
X3 = X1 + X2
X4 = X1 - X2
plt.subplot(121)
X3.plot_spectrogram()
plt.subplot(122)
X4.plot_spectrogram()

plt.figure()
X3 = X1 * X2
X4 = X1 / X2
plt.subplot(121)
X3.plot_spectrogram()
plt.subplot(122)
X4.plot_spectrogram()
pass
../_images/_notebooks_tf_data_19_0.png
../_images/_notebooks_tf_data_19_1.png
../_images/_notebooks_tf_data_19_2.png