Skip to content
Snippets Groups Projects
Commit 18f7987f authored by Florent Jaillet's avatar Florent Jaillet
Browse files

Initial release of pyteuf v1.0.0

parents
No related branches found
No related tags found
No related merge requests found
Pipeline #1494 passed
Showing with 2074 additions and 0 deletions
[run]
branch = True
source = pyteuf
include = */pyteuf/*
omit =
*/setup.py
*/__init__.py
*/test_*
[report]
exclude_lines =
pragma: no cover
if self.debug:
if settings.DEBUG
raise AssertionError
raise NotImplementedError
if 0:
if __name__ == .__main__.:
if obj is None: return
if verbose > 0:
if self.verbose > 0:
if verbose > 1:
if self.verbose > 1:
pass
def __str__(self):
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Mac OS X files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Sphinx stuff
docs/generated/
docs/build/
# jupyter notebook
.ipynb_checkpoints
# coverage
htmlcov
.coverage
# ide
.idea
.spyderproject
# Misc
.cache
.pytest_cache
# run the test suite
tests:
image: registry.gitlab.lis-lab.fr:5005/skmad-suite/madarrays/ubuntu:18.04
tags:
- docker
script:
- pip3 install --no-deps ltfatpy madarrays
- pip3 install --no-deps .
- pytest-3
# generate the documentation
pages:
image: registry.gitlab.lis-lab.fr:5005/skmad-suite/madarrays/ubuntu:18.04
tags:
- docker
only:
- master
script:
- pip3 install --no-deps ltfatpy madarrays
- pip3 install --no-deps .
- python3 setup.py build_sphinx
- cp -r build/sphinx/html public
artifacts:
paths:
- public
This diff is collapsed.
include *.txt
include *.rst
include VERSION
recursive-include doc *.rst *.py *.ipynb
include pyteuf/tests/*.py
prune doc/build
pyteuf
======
pyteuf is a package for time-frequency analysis in Python.
Install
-------
Install the current release with ``pip``::
pip install pyteuf
For additional details, see doc/install.rst.
Usage
-----
See the `documentation <http://skmad-suite.pages.lis-lab.fr/pyteuf/>`_.
Bugs
----
Please report any bugs that you find through the `pyteuf GitLab project
<https://gitlab.lis-lab.fr/skmad-suite/pyteuf/issues>`_.
You can also fork the repository and create a merge request.
Source code
-----------
The source code of pyteuf is available via its `GitLab project
<https://gitlab.lis-lab.fr/skmad-suite/pyteuf>`_.
You can clone the git repository of the project using the command::
git clone git@gitlab.lis-lab.fr:skmad-suite/pyteuf.git
Copyright © 2017-2018
---------------------
* `Laboratoire d'Informatique et Systèmes <http://www.lis-lab.fr/>`_
* `Université d'Aix-Marseille <http://www.univ-amu.fr/>`_
* `Centre National de la Recherche Scientifique <http://www.cnrs.fr/>`_
* `Université de Toulon <http://www.univ-tln.fr/>`_
Contributors
------------
* `Ronan Hamon <mailto:ronan.hamon@lis-lab.fr>`_
* `Valentin Emiya <mailto:valentin.emiya@lis-lab.fr>`_
* `Florent Jaillet <mailto:florent.jaillet@lis-lab.fr>`_
License
-------
Released under the GNU General Public License version 3 or later
(see `LICENSE.txt`).
pyteuf:1.0.0
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -m sphinx
PAPER =
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest epub
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " pickle to make pickle files"
@echo " epub to make an epub"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " gitwash to update the gitwash documentation"
clean:
-rm -rf build/*
-rm -rf ghpages_build
-rm -rf auto_examples modules
-rm -rf reference/generated reference/algorithms/generated reference/classes/generated reference/readwrite/generated
dist: html
test -d build/latex || make latex
make -C build/latex all-pdf
-rm -rf build/dist
(cd build/html; cp -r . ../../build/dist)
(cd build/dist && tar czf ../dist.tar.gz .)
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
@echo
@echo "Build finished. The HTML pages are in build/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) build/dirhtml
@echo
@echo "Build finished. The HTML pages are in build/dirhtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in build/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in build/qthelp, like this:"
@echo "# qcollectiongenerator build/qthelp/test.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile build/qthelp/test.qhc"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) build/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
@echo
@echo "Build finished; the LaTeX files are in build/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
@echo
@echo "The overview file is in build/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in build/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) build/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in build/doctest/output.txt."
latexpdf: latex
@echo "Running LaTeX files through latexmk..."
$(MAKE) -C build/latex all-pdf
@echo "latexmk finished; the PDF files are in build/latex."
docs: clean html latexpdf
cp build/latex/networkx_reference.pdf build/html/_downloads/.
gitwash-update:
python ../tools/gitwash_dumper.py developer networkx \
--project-url=http://networkx.github.io \
--project-ml-url=http://groups.google.com/group/networkx-discuss/ \
--gitwash-url git@github.com:matthew-brett/gitwash.git
If you only want to get the documentation, note that a pre-built
version for the latest release is available
[online](http://skmad-suite.pages.lis-lab.fr/pyteuf/).
Sphinx is used to generate the API and reference documentation for all
packages from the [`skmad-suite`](https://gitlab.lis-lab.fr/skmad-suite/).
## Instructions to build the documentation
In addition to installing ``pyteuf`` and its dependencies, install the
Python packages needed to build the documentation by entering
```
pip install -r ../requirements/doc.txt
```
in the ``doc/`` directory.
To build the HTML documentation, run:
```
make html
```
in the ``doc/`` directory. This will generate a ``build/html`` subdirectory
containing the built documentation.
To build the PDF documentation, run:
```
make latexpdf
```
You will need to have Latex installed for this.
%% Cell type:code id: tags:
``` python
%pylab inline
```
%% Cell type:markdown id: tags:
# ``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.
%% Cell type:code id: tags:
``` python
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}
```
%% Cell type:markdown id: tags:
## Example of TF representation of a complex signal, with binary masking
%% Cell type:markdown id: tags:
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
%% Cell type:code id: tags:
``` python
# 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)
```
%% Cell type:markdown id: tags:
## Example of TF representation of a real signal, with binary masking
%% Cell type:markdown id: tags:
For a real signal, only the non-negative frequencies area is handled due to Hermitian symetry.
%% Cell type:code id: tags:
``` python
# 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)
```
%% Cell type:markdown id: tags:
## Example of TF representation with complex masking
%% Cell type:markdown id: tags:
With complex masking, two masks are handled: a mask for amplitudes and a mask for phases.
%% Cell type:code id: tags:
``` python
# 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)
```
%% Cell type:markdown id: tags:
One may select one of the masks or a combination of them for display.
%% Cell type:code id: tags:
``` python
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))
```
%% Cell type:markdown id: tags:
Selecting a mask type can also be used to extract the related mask:
%% Cell type:code id: tags:
``` python
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)))
```
%% Cell type:markdown id: tags:
Other useful properties:
%% Cell type:code id: tags:
``` python
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))
```
%% Cell type:markdown id: tags:
Current operations between two ``TfData`` objects are not recommended since resulting operations on masks are not designed adequately:
%% Cell type:code id: tags:
``` python
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
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
%pylab inline
```
%% Cell type:markdown id: tags:
# 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).
%% Cell type:code id: tags:
``` python
from pyteuf import Stft
from madarrays import Waveform
```
%% Cell type:code id: tags:
``` python
stft = Stft(hop=16, n_bins=512, win_name='sine', win_len=256,
param_constraint='pad', zero_pad_full_sig=False)
print(stft)
```
%% Cell type:markdown id: tags:
The inverse transform may obtained from the direct transform by
%% Cell type:code id: tags:
``` python
istft = stft.get_istft()
print(istft)
```
%% Cell type:markdown id: tags:
### Example on a synthetic real signal
%% Cell type:markdown id: tags:
Create a signal composed of two sines
%% Cell type:code id: tags:
``` python
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)
```
%% Cell type:markdown id: tags:
Compute STFT and display the spectrogram
%% Cell type:code id: tags:
``` python
X = stft.apply(x, fs=fs)
print(X)
_ = X.plot_spectrogram(dynrange=100.)
```
%% Cell type:markdown id: tags:
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:
%% Cell type:code id: tags:
``` python
y = istft.apply(X)
print('Reconstruction: {}'.format(y))
print('Reconstruction error: {:.3f} dB'.format(10 * np.log10(np.mean(np.abs(x - y)**2))))
```
%% Cell type:markdown id: tags:
### Example on a synthetic complex signal
%% Cell type:markdown id: tags:
Create a waveform composed of two sines
%% Cell type:code id: tags:
``` python
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()
```
%% Cell type:code id: tags:
``` python
X = stft.apply(x)
print(X)
_ = X.plot_spectrogram(dynrange=100.)
```
%% Cell type:markdown id: tags:
Note that all the frequencies are shown since the signal is complex.
Reconstruction is obtained by:
%% Cell type:code id: tags:
``` python
y = istft.apply(X)
print('Reconstruction: {}'.format(y))
print('Reconstruction error: {:.3f} dB'.format(10 * np.log10(np.mean(np.abs(x - y)**2))))
```
%% Cell type:markdown id: tags:
### Example on a real sound
%% Cell type:markdown id: tags:
Load test sound
%% Cell type:code id: tags:
``` python
from ltfatpy import gspi
x, fs = gspi()
x = Waveform(x, fs=fs)
```
%% Cell type:markdown id: tags:
Note that one may also use static method `Waveform.from_wavfile(filename)` in order to load a `Waveform` object from an audio file.
%% Cell type:markdown id: tags:
Apply STFT and display properties of Stft data
%% Cell type:code id: tags:
``` python
X = stft.apply(x)
print(X)
```
%% Cell type:markdown id: tags:
Get a related Istft object, apply istft, display properties of reconstruction
%% Cell type:code id: tags:
``` python
y = istft.apply(X)
print('Reconstruction: {}'.format(y))
print('Reconstruction error: {:.3f} dB'.format(10 * np.log10(np.mean(np.abs(x - y)**2))))
```
%% Cell type:markdown id: tags:
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.
%% Cell type:code id: tags:
``` python
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')
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
# Time-frequency transforms : boundary effects
The examples below illustrate the effects of setting Stft-object flag zero_pad_full_sig to True (False) in order to avoid (obtain) a circular transform.
%% Cell type:code id: tags:
``` python
%pylab inline
import numpy as np
from madarrays import Waveform
from pyteuf import Stft
```
%% Cell type:markdown id: tags:
## Examples with isolated diracs
The signal with length 16 samples is composed of a dirac at initial time $t=0$ for testing the boundary effects and another dirac time $t=9$ for control.
%% Cell type:code id: tags:
``` python
signal_params_nopad = {'sig_len': 16, 'fs': 1}
x_nopad = Waveform(np.zeros(signal_params_nopad['sig_len']),
fs = signal_params_nopad['fs'])
x_nopad[0] = x_nopad[9] = 1
print(x_nopad)
x_nopad.plot()
```
%% Cell type:code id: tags:
``` python
# Note that the signal length is increased by 1 in order
# to satisfy the `param_constraint='fix'` parameter of Stft (see tutorial on transform length)
signal_params_pad = {'sig_len': signal_params_nopad['sig_len']+1, 'fs': signal_params_nopad['fs']}
x_pad = Waveform(np.zeros(signal_params_pad['sig_len']),
fs = signal_params_pad['fs'])
x_pad[0] = x_pad[9] = 1
print(x_pad)
x_pad.plot()
```
%% Cell type:markdown id: tags:
Two similar `Stft` objects are defined, that differ only on the use of additional zero-padding. By setting `zero_pad_full_sig=False`, no additional zero-padding results in a circular transform. By setting `zero_pad_full_sig=True`, zeros are added at the end of the signal in an appropriate way such that the samples at the beginning and at the end of the original signal are not appearing in the same frame (i.e., the number of added zeros equals the window length minus one).
%% Cell type:code id: tags:
``` python
stft_params = {'hop': 1, 'n_bins': 4, 'win_name': 'hann', 'win_len': 4, 'param_constraint': 'fix'}
stft_nopad = Stft(zero_pad_full_sig=False, **stft_params)
print(stft_nopad)
stft_pad = Stft(zero_pad_full_sig=True, **stft_params)
print(stft_pad)
```
%% Cell type:markdown id: tags:
With the circular transform, the dirac at time $t=0$ spread at times $t=0$, $t=1$ and $t=15$ (i.e., at the last time step in the signal with length 16)
%% Cell type:code id: tags:
``` python
X_nopad = stft_nopad.apply(x_nopad)
print(X_nopad)
_ = X_nopad.plot_spectrogram(dynrange=100)
```
%% Cell type:markdown id: tags:
With additional zero-padding, the dirac at time $t=0$ does not spread at $t=15$ any more since the signal has been zero-padded to avoid circular effects (even if the transform is still circular). Energy is observed at time $t=19$, i.e. in the area where the signal has been extended.
%% Cell type:code id: tags:
``` python
X_pad = stft_pad.apply(x_pad)
print(X_pad)
_ = X_pad.plot_spectrogram(dynrange=100)
```
%% Cell type:markdown id: tags:
## Examples with diracs at boundaries
This example illustrate how the first and last samples are neighbors in the case of a circular transform.
The signal is now composed of a dirac at each boundary, i.e., at times $t=0$ and $t=15$, as well as two neighboring diracs at times $t=8$ and $t=9$ for control.
%% Cell type:code id: tags:
``` python
x_nopad = Waveform(np.zeros(signal_params_nopad['sig_len']),
fs = signal_params_nopad['fs'])
x_nopad[0] = x_nopad[-1] = x_nopad[8] = x_nopad[9] = 1
print(x_nopad)
x_nopad.plot()
```
%% Cell type:code id: tags:
``` python
x_pad = Waveform(np.zeros(signal_params_pad['sig_len']),
fs = signal_params_pad['fs'])
x_pad[0] = x_pad[-1] = x_pad[8] = x_pad[9] = 1
print(x_pad)
x_pad.plot()
```
%% Cell type:markdown id: tags:
Without zero-padding, due to the circular transform, diracs influence each other and form a single area in the spectrogram, which is similar as the center area that contains two adjacent diracs.
%% Cell type:code id: tags:
``` python
stft_nopad.apply(x_nopad).plot_spectrogram(dynrange=100)
```
%% Cell type:markdown id: tags:
With additional zero-padding, the beginning and the end of the signal are separated with zeros so diracs are well resolved and do not appear as adjacent diracs.
%% Cell type:code id: tags:
``` python
_ = stft_pad.apply(x_pad).plot_spectrogram(dynrange=100)
```
%% Cell type:markdown id: tags:
## Examples with a sinusoid
The signal is now a sinusoid and the length of the signal is a multiple of the period.
%% Cell type:code id: tags:
``` python
signal_params_nopad = {'sig_len': 32}
signal_params_nopad['fs'] = signal_params_nopad['sig_len'] - 1
signal_params_pad = {'sig_len': signal_params_nopad['sig_len'] + 1}
signal_params_pad['fs'] = signal_params_pad['sig_len'] - 1
f0 = 4 / signal_params_nopad['sig_len']
x_nopad = Waveform(np.sin(2 * np.pi * f0 * np.arange(signal_params_nopad['sig_len'])),
fs = signal_params_nopad['fs'])
x_nopad.plot()
f0_pad = 4 / signal_params_pad['sig_len']
x_pad = Waveform(np.sin(2 * np.pi * f0_pad * np.arange(signal_params_pad['sig_len'])),
fs = signal_params_pad['fs'])
x_pad.plot(linestyle='--')
```
%% Cell type:code id: tags:
``` python
stft_params = {'hop': 1, 'n_bins': 16, 'win_name': 'hann', 'win_len': 16, 'param_constraint': 'fix'}
stft_nopad = Stft(zero_pad_full_sig=False, **stft_params)
print(stft_nopad)
stft_pad = Stft(zero_pad_full_sig=True, **stft_params)
print(stft_pad)
```
%% Cell type:markdown id: tags:
Periodic tranform: as the length of the signal is a multiple of the period, the circular transform coefficients are constant over time.
%% Cell type:code id: tags:
``` python
X_nopad = stft_nopad.apply(x_nopad)
print(X_nopad)
_ = X_nopad.plot_spectrogram(dynrange=100)
```
%% Cell type:markdown id: tags:
With additional zeros to avoid circular effects, transients appear at boundaries.
%% Cell type:code id: tags:
``` python
X_pad = stft_pad.apply(x_pad)
print(X_pad)
_ = X_pad.plot_spectrogram(dynrange=100)
```
%% Cell type:markdown id: tags:
Now, if the period is not a divisor of the length of the signal:
%% Cell type:code id: tags:
``` python
signal_params_nopad = {'sig_len': 32}
signal_params_nopad['fs'] = signal_params_nopad['sig_len'] - 1
signal_params_pad = {'sig_len': signal_params_nopad['sig_len'] + 1}
signal_params_pad['fs'] = signal_params_pad['sig_len'] - 1
f0 = 8.5 / signal_params_nopad['sig_len']
x_nopad = Waveform(np.sin(2 * np.pi * f0 * np.arange(signal_params_nopad['sig_len'])),
fs = signal_params_nopad['fs'])
x_nopad.plot()
f0_pad = 8.5 / signal_params_pad['sig_len']
x_pad = Waveform(np.sin(2 * np.pi * f0_pad * np.arange(signal_params_pad['sig_len'])),
fs = signal_params_pad['fs'])
x_pad.plot(linestyle='--')
```
%% Cell type:markdown id: tags:
Some boundary effects appear in the case without zero-padding due to the discontinuity between the end and the beginning of the signal:
%% Cell type:code id: tags:
``` python
X_nopad = stft_nopad.apply(x_nopad)
print(X_nopad)
_ = X_nopad.plot_spectrogram(dynrange=100)
```
%% Cell type:markdown id: tags:
Some boundary effects also appear in the case with zero-padding due to the discontinuity with the added zeros:
%% Cell type:code id: tags:
``` python
X_pad = stft_pad.apply(x_pad)
print(X_pad)
_ = X_pad.plot_spectrogram(dynrange=100)
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
# 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.
%% Cell type:code id: tags:
``` python
%pylab inline
import numpy as np
from madarrays import Waveform
from pyteuf import Stft
```
%% Cell type:markdown id: tags:
## Case 1 : parameters fitting the constraints
%% Cell type:markdown id: tags:
Let us define a signal composed of a pure sine
%% Cell type:code id: tags:
``` python
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()
```
%% Cell type:markdown id: tags:
We use three similar `Stft` that only differ in the `param_constraint` option.
%% Cell type:code id: tags:
``` python
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)
```
%% Cell type:markdown id: tags:
Since the constraint is satisfied, `stft_fix` and `stft_pad` transforms give the same results
%% Cell type:code id: tags:
``` python
_ = stft_fix.apply(x).plot_spectrogram(dynrange=100.)
```
%% Cell type:code id: tags:
``` python
_ = stft_pad.apply(x).plot_spectrogram(dynrange=100.)
```
%% Cell type:markdown id: tags:
Since the hop size and the number of bins are powers of 2, the results by `stft_pow2` is also the same.
%% Cell type:code id: tags:
``` python
_ = stft_pow2.apply(x).plot_spectrogram(dynrange=100.)
```
%% Cell type:markdown id: tags:
## Case 2: parameters not fitting the constraints
%% Cell type:markdown id: tags:
Again, the signal is composed of a pure sine, but its size is different.
%% Cell type:code id: tags:
``` python
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()
```
%% Cell type:markdown id: tags:
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.
%% Cell type:code id: tags:
``` python
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)
```
%% Cell type:markdown id: tags:
For `param_constraint='fix'`, an error is raised since the signal length is not a multiple of the hop length.
%% Cell type:code id: tags:
``` python
try:
X_fix = stft_fix.apply(x)
except BaseException as e:
print('Error raised:', e)
```
%% Cell type:markdown id: tags:
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.
%% Cell type:code id: tags:
``` python
X_pad = stft_pad.apply(x)
print(X_pad)
_ = X_pad.plot_spectrogram(dynrange=100.)
```
%% Cell type:markdown id: tags:
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.
%% Cell type:code id: tags:
``` python
X_pow2 = stft_pow2.apply(x)
print(X_pow2)
_ = X_pow2.plot_spectrogram(dynrange=100.)
```
%% Cell type:code id: tags:
``` python
```
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import os
from datetime import date
import pyteuf
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../pyteuf/'))
# -- General configuration ------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode',
'numpydoc',
'nbsphinx',
'IPython.sphinxext.ipython_console_highlighting',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'pyteuf'
author = 'V. Emiya, R. Hamon, F. Jaillet'
copyright = '2017-{}, {}'.format(date.today().year, author)
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = pyteuf.__version__
# The full version, including alpha/beta/rc tags.
release = pyteuf.__version__.replace('_', '')
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build', '**/test_*.rst', '**.ipynb_checkpoints']
# The reST default role (used for this markup: `text`) to use for all
# documents.
default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'bizstyle'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'
# Custom sidebar templates, maps document names to template names.
html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
html_additional_pages = {}
# If false, no module index is generated.
html_domain_indices = True
# If false, no index is generated.
html_use_index = True
# If true, the index is split into individual pages for each letter.
html_split_index = False
# If true, links to the reST sources are added to the pages.
html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
html_search_scorer = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'pyteufdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
'papersize': 'a4paper',
# The font size ('10pt', '11pt' or '12pt').
'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
'preamble': '',
# Latex figure (float) alignment
'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'pyteuf.tex', 'pyteuf Documentation',
'V. Emiya, R. Hamon., F. Jaillet', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
latex_use_parts = False
# If true, show page references after internal links.
latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
latex_appendices = []
# If false, no module index is generated.
latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, 'pyteuf', 'pyteuf Documentation', [author], 1)]
# If true, show URL addresses after external links.
man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'pyteuf', 'pyteuf Documentation', author, 'pyteuf',
'One line description of project.', 'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
texinfo_appendices = []
# If false, no module index is generated.
texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
texinfo_no_detailmenu = False
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
'python': ('https://docs.python.org/3/', None),
'numpy': ('https://docs.scipy.org/doc/numpy/', None),
'scipy': ('https://docs.scipy.org/doc/scipy/reference/', None),
'matplotlib': ('https://matplotlib.org/', None),
'madarrays': ('http://skmad-suite.pages.lis-lab.fr/madarrays/', None),
'ltfatpy': ('http://dev.pages.lis-lab.fr/ltfatpy/', None),
}
# Allow errors in notebook
nbsphinx_allow_errors = True
# Do not show class members
numpydoc_show_class_members = False
# Include todos
todo_include_todos = True
# Order members by member type
autodoc_member_order = 'groupwise'
Credits
=======
Copyright(c) 2018
-----------------
* Laboratoire d'Informatique et Systèmes <http://www.lis-lab.fr/>
* Université d'Aix-Marseille <http://www.univ-amu.fr/>
* Centre National de la Recherche Scientifique <http://www.cnrs.fr/>
* Université de Toulon <http://www.univ-tln.fr/>
Contributors
------------
* Ronan Hamon <firstname.lastname_AT_lis-lab.fr>
* Valentin Emiya <firstname.lastname_AT_lis-lab.fr>
* Florent Jaillet <firstname.lastname_AT_lis-lab.fr>
Licence
-------
This file is part of pyteuf.
pyteuf is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
###########################
:mod:`pyteuf` documentation
###########################
Overview
========
:mod:`pyteuf` is a package for time-frequency analysis/synthesis with possibly
missing data in Python.
:mod:`pyteuf` provides:
* a time-frequency 2D data structure :class:`TfData <pyteuf.tf_data.TfData>` to
manipulate time-frequency representations, either complex (STFT, etc.) or
real (spectrograms, etc.) in which some coefficients may be missing
(missing amplitude, missing phase, or both).
* a class :class:`Stft <pyteuf.tf_transforms.Stft>` and a class
:class:`Istft <pyteuf.tf_transforms.Istft>`, which are an interface to
:mod:`ltfatpy` in order to facilitate the use of direct and inverse
short-time Fourier transforms.
Documentation
=============
.. only:: html
:Release: |version|
:Date: |today|
.. toctree::
:maxdepth: 1
installation
references
tutorials
credits
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
Installation
############
``pyteuf`` requires the following packages:
* `python >= 3.5 <https://wiki.python.org/moin/BeginnersGuide/Download>`_
* `ltfatpy >= 1.0.15 <https://gitlab.lis-lab.fr/dev/ltfatpy>`_
* `madarrays >= 1.1 <https://gitlab.lis-lab.fr/skmad-suite/madarrays>`_
* `matplotlib >= 2.1 <http://matplotlib.org/>`_
* `numpy >= 1.13 <http://www.numpy.org>`_
Make sure your Python environment is properly configured. It is recommended to
install ``pyteuf`` in a virtual environment.
Release version
---------------
First, make sure you have the latest version of pip (the Python package
manager) installed. If you do not, refer to the `Pip documentation
<https://pip.pypa.io/en/stable/installing/>`_ and install ``pip`` first.
Install the current release with ``pip``::
pip install pyteuf
To upgrade to a newer release use the ``--upgrade`` flag::
pip install --upgrade pyteuf
If you do not have permission to install software systemwide, you can install
into your user directory using the ``--user`` flag::
pip install --user pyteuf
Alternatively, you can manually download ``pyteuf`` from its `GitLab project
<https://gitlab.lis-lab.fr/skmad-suite/pyteuf>`_ or `PyPI
<https://pypi.python.org/pypi/pyteuf>`_. To install one of these versions,
unpack it and run the following from the top-level source directory using the
Terminal::
pip install .
Development version
-------------------
If you have `Git <https://git-scm.com/>`_ installed on your system, it is also
possible to install the development version of ``pyteuf``.
Before installing the development version, you may need to uninstall the
standard version of ``pyteuf`` using ``pip``::
pip uninstall pyteuf
Clone the Git repository::
git clone git@gitlab.lis-lab.fr:skmad-suite/pyteuf.git
cd pyteuf
You may also need to install required packages::
pip install -r requirements/defaults.txt
Then execute ``pip`` with flag ``-e`` to follow the development branch::
pip install -e .
To update ``pyteuf`` at any time, in the same directory do::
git pull
To run unitary tests, first install required packages::
pip install -r requirements/dev.txt
and execute ``pytest``::
pytest
References
==========
:Release: |release|
:Date: |today|
pyteuf\.tf_transforms module
----------------------------
.. automodule:: pyteuf.tf_transforms
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
pyteuf\.tf_data module
----------------------
.. automodule:: pyteuf.tf_data
:members:
:undoc-members:
:show-inheritance:
pyteuf\.utils module
--------------------
.. automodule:: pyteuf.utils
:members:
:undoc-members:
:show-inheritance:
Tutorials
#########
.. toctree::
:maxdepth: 1
_notebooks/tf_data.ipynb
_notebooks/time_frequency.ipynb
_notebooks/time_frequency_boundary_effects.ipynb
_notebooks/time_frequency_transform_length_constraint.ipynb
"""Time-frequency analysis in Python
.. moduleauthor:: Valentin Emiya
.. moduleauthor:: Ronan Hamon
.. moduleauthor:: Florent Jaillet
"""
from .tf_data import TfData
from .tf_transforms import Stft
from .tf_transforms import Istft
__all__ = ['Stft', 'Istft', 'TfData']
__version__ = "1.0.0"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment