├── .github
└── workflows
│ ├── test_conda.yml
│ └── test_pip.yml
├── .gitignore
├── LICENCE
├── README.md
├── data
└── DCS_LI_QuartetB_Take03_S1_LRX_excerpt.wav
├── demo_libf0.ipynb
├── docs
├── .nojekyll
├── Makefile
├── build
│ └── html
│ │ ├── .buildinfo
│ │ ├── .doctrees
│ │ ├── environment.pickle
│ │ ├── getting_started.doctree
│ │ ├── index.doctree
│ │ ├── index_pyin.doctree
│ │ ├── index_salience.doctree
│ │ ├── index_swipe.doctree
│ │ ├── index_swipe_slim.doctree
│ │ ├── index_utils.doctree
│ │ └── index_yin.doctree
│ │ ├── _modules
│ │ ├── index.html
│ │ └── libf0
│ │ │ ├── pyin.html
│ │ │ ├── salience.html
│ │ │ ├── swipe.html
│ │ │ ├── swipe_slim.html
│ │ │ ├── utils.html
│ │ │ └── yin.html
│ │ ├── _sources
│ │ ├── getting_started.rst.txt
│ │ ├── index.rst.txt
│ │ ├── index_pyin.rst.txt
│ │ ├── index_salience.rst.txt
│ │ ├── index_swipe.rst.txt
│ │ ├── index_swipe_slim.rst.txt
│ │ ├── index_utils.rst.txt
│ │ └── index_yin.rst.txt
│ │ ├── _static
│ │ ├── _sphinx_javascript_frameworks_compat.js
│ │ ├── basic.css
│ │ ├── css
│ │ │ ├── badge_only.css
│ │ │ ├── fonts
│ │ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ │ ├── Roboto-Slab-Regular.woff2
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.svg
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ ├── fontawesome-webfont.woff
│ │ │ │ ├── fontawesome-webfont.woff2
│ │ │ │ ├── lato-bold-italic.woff
│ │ │ │ ├── lato-bold-italic.woff2
│ │ │ │ ├── lato-bold.woff
│ │ │ │ ├── lato-bold.woff2
│ │ │ │ ├── lato-normal-italic.woff
│ │ │ │ ├── lato-normal-italic.woff2
│ │ │ │ ├── lato-normal.woff
│ │ │ │ └── lato-normal.woff2
│ │ │ └── theme.css
│ │ ├── doctools.js
│ │ ├── documentation_options.js
│ │ ├── file.png
│ │ ├── fonts
│ │ │ ├── FontAwesome.otf
│ │ │ ├── Lato
│ │ │ │ ├── lato-bold.eot
│ │ │ │ ├── lato-bold.ttf
│ │ │ │ ├── lato-bold.woff
│ │ │ │ ├── lato-bold.woff2
│ │ │ │ ├── lato-bolditalic.eot
│ │ │ │ ├── lato-bolditalic.ttf
│ │ │ │ ├── lato-bolditalic.woff
│ │ │ │ ├── lato-bolditalic.woff2
│ │ │ │ ├── lato-italic.eot
│ │ │ │ ├── lato-italic.ttf
│ │ │ │ ├── lato-italic.woff
│ │ │ │ ├── lato-italic.woff2
│ │ │ │ ├── lato-regular.eot
│ │ │ │ ├── lato-regular.ttf
│ │ │ │ ├── lato-regular.woff
│ │ │ │ └── lato-regular.woff2
│ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ ├── Roboto-Slab-Light.woff
│ │ │ ├── Roboto-Slab-Light.woff2
│ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ ├── Roboto-Slab-Regular.woff2
│ │ │ ├── Roboto-Slab-Thin.woff
│ │ │ ├── Roboto-Slab-Thin.woff2
│ │ │ ├── RobotoSlab
│ │ │ │ ├── roboto-slab-v7-bold.eot
│ │ │ │ ├── roboto-slab-v7-bold.ttf
│ │ │ │ ├── roboto-slab-v7-bold.woff
│ │ │ │ ├── roboto-slab-v7-bold.woff2
│ │ │ │ ├── roboto-slab-v7-regular.eot
│ │ │ │ ├── roboto-slab-v7-regular.ttf
│ │ │ │ ├── roboto-slab-v7-regular.woff
│ │ │ │ └── roboto-slab-v7-regular.woff2
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.svg
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ ├── fontawesome-webfont.woff2
│ │ │ ├── lato-bold-italic.woff
│ │ │ ├── lato-bold-italic.woff2
│ │ │ ├── lato-bold.woff
│ │ │ ├── lato-bold.woff2
│ │ │ ├── lato-normal-italic.woff
│ │ │ ├── lato-normal-italic.woff2
│ │ │ ├── lato-normal.woff
│ │ │ └── lato-normal.woff2
│ │ ├── jquery-3.5.1.js
│ │ ├── jquery-3.6.0.js
│ │ ├── jquery.js
│ │ ├── js
│ │ │ ├── badge_only.js
│ │ │ ├── html5shiv-printshiv.min.js
│ │ │ ├── html5shiv.min.js
│ │ │ ├── modernizr.min.js
│ │ │ └── theme.js
│ │ ├── language_data.js
│ │ ├── libf0.png
│ │ ├── minus.png
│ │ ├── plus.png
│ │ ├── pygments.css
│ │ ├── searchtools.js
│ │ ├── sphinx_highlight.js
│ │ ├── underscore-1.12.0.js
│ │ ├── underscore-1.13.1.js
│ │ └── underscore.js
│ │ ├── genindex.html
│ │ ├── getting_started.html
│ │ ├── index.html
│ │ ├── index_pyin.html
│ │ ├── index_salience.html
│ │ ├── index_swipe.html
│ │ ├── index_swipe_slim.html
│ │ ├── index_utils.html
│ │ ├── index_yin.html
│ │ ├── objects.inv
│ │ ├── py-modindex.html
│ │ ├── search.html
│ │ └── searchindex.js
├── index.html
├── make.bat
└── source
│ ├── _static
│ └── libf0.png
│ ├── conf.py
│ ├── getting_started.rst
│ ├── index.rst
│ ├── index_pyin.rst
│ ├── index_salience.rst
│ ├── index_swipe.rst
│ ├── index_swipe_slim.rst
│ ├── index_utils.rst
│ └── index_yin.rst
├── libf0
├── __init__.py
├── pyin.py
├── salience.py
├── swipe.py
├── swipe_slim.py
├── utils.py
└── yin.py
├── setup.py
└── tests
├── __init__.py
├── __pycache__
└── conftest.cpython-38-pytest-7.1.2.pyc
└── test_algorithms.py
/.github/workflows/test_conda.yml:
--------------------------------------------------------------------------------
1 | name: build conda
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build-linux:
11 | runs-on: ubuntu-latest
12 | strategy:
13 | max-parallel: 5
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Set up Python 3.8
18 | uses: actions/setup-python@v2
19 | with:
20 | python-version: 3.8
21 | - name: Add conda to system path
22 | run: |
23 | # $CONDA is an environment variable pointing to the root of the miniconda directory
24 | echo $CONDA/bin >> $GITHUB_PATH
25 | - name: Install dependencies
26 | run: |
27 | conda install -c conda-forge libsndfile python=3.8
28 | python -m pip install -e .['tests']
29 | - name: Lint with flake8
30 | run: |
31 | conda install flake8
32 | # stop the build if there are Python syntax errors or undefined names
33 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
34 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
35 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
36 | - name: Test with pytest
37 | run: |
38 | pytest
39 |
--------------------------------------------------------------------------------
/.github/workflows/test_pip.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3 |
4 | name: build
5 |
6 | on:
7 | push:
8 | branches: [ main ]
9 | pull_request:
10 | branches: [ main ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | python-version: [3.7, 3.8, 3.9]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Set up Python ${{ matrix.python-version }}
24 | uses: actions/setup-python@v2
25 | with:
26 | python-version: ${{ matrix.python-version }}
27 | - name: Install libnsdfile
28 | run: |
29 | sudo apt update
30 | sudo apt install libsndfile1-dev libsndfile1
31 | - name: Install dependencies
32 | run: |
33 | python -m pip install --upgrade pip
34 | python -m pip install flake8
35 | python -m pip install -e .['tests']
36 | python --version
37 | pip --version
38 | python -m pip list
39 | - name: Lint with flake8
40 | run: |
41 | # stop the build if there are Python syntax errors or undefined names
42 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
43 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
44 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
45 | - name: Test with pytest
46 | run: |
47 | pytest
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .DS_Store
3 | .idea
4 |
5 | # ignore files created when preparing package for PyPi
6 | build/
7 | dist/
8 | *.egg-info/
9 |
10 | # ignore cache files
11 | .ipynb_checkpoints
12 | .pytest_cache
13 |
14 | # do not ignore build of docs
15 | !docs/build/
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Sebastian Rosenzweig, Simon Schwär, Meinard Müller, International Audio Laboratories Erlangen, Germany.
4 | We thank the German Research Foundation (DFG) for various research grants that
5 | allow us for conducting fundamental research in music processing.
6 | The International Audio Laboratories Erlangen are a joint institution of the
7 | Friedrich-Alexander-Universität Erlangen-Nürnberg (FAU) and Fraunhofer
8 | Institute for Integrated Circuits IIS.
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy of
11 | this software and associated documentation files (the "Software"), to deal in
12 | the Software without restriction, including without limitation the rights to
13 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
14 | the Software, and to permit persons to whom the Software is furnished to do so,
15 | subject to the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
22 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
23 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/groupmm/libf0/actions/workflows/test_conda.yml)
2 | [](https://github.com/groupmm/libf0/actions/workflows/test_pip.yml)
3 |
4 |
5 | # libf0
6 |
7 | This repository contains a Python package called libf0 which provides open-source implementations for four popular model-based F0-estimation approaches, YIN (Cheveigné & Kawahara, 2002), pYIN (Mauch & Dixon, 2014), an approach inspired by Melodia (Salamon & Gómez, 2012), and SWIPE (Camacho & Harris, 2008).
8 |
9 | If you use the libf0 in your research, please consider the following references.
10 |
11 | ## References
12 |
13 | Sebastian Rosenzweig, Simon Schwär, and Meinard Müller.
14 | [libf0: A Python Library for Fundamental Frequency Estimation.](https://archives.ismir.net/ismir2022/latebreaking/000003.pdf)
15 | In Late Breaking Demos of the International Society for Music Information Retrieval Conference (ISMIR), Bengaluru, India, 2022.
16 |
17 | Alain de Cheveigné and Hideki Kawahara.
18 | YIN, a fundamental frequency estimator for speech and music. Journal of the Acoustical Society of America (JASA), 111(4):1917–1930, 2002.
19 |
20 | Matthias Mauch and Simon Dixon.
21 | pYIN: A fundamental frequency estimator using probabilistic threshold distributions. In IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), pages 659–663, Florence, Italy, 2014.
22 |
23 | Justin Salamon and Emilia Gómez.
24 | Melody extraction from polyphonic music signals using pitch contour characteristics. IEEE Transactions on Audio, Speech, and Language Processing, 20(6):
25 | 1759–1770, 2012.
26 |
27 | Arturo Camacho and John G. Harris.
28 | A sawtooth waveform inspired pitch estimator for speech and music. The Journal of the Acoustical Society of America, 124(3):1638–1652, 2008.
29 |
30 | Meinard Müller. Fundamentals of Music Processing – Using Python and Jupyter Notebooks. Springer Verlag, 2nd edition, 2021. ISBN 978-3-030-69807-2. doi: 10.1007/978-3-030-69808-9.
31 |
32 |
33 | ## Installing
34 |
35 | If you just want to try our example notebook, you can run it using Binder directly in your browser: [](https://mybinder.org/v2/gh/groupmm/libf0/HEAD)
36 |
37 | To install the libf0 locally, you can use the Python package manager pip:
38 |
39 | ```
40 | pip install libf0
41 | ```
42 |
43 | We recommend to do this inside a conda or virtual environment (requiring at least Python 3.7).
44 | If you want to run the example notebook locally, you **must** first install libf0 to resolve all dependencies. Then, you can clone this repository using
45 |
46 | ```
47 | git clone https://github.com/groupmm/libf0.git
48 | ```
49 | install Jupyter using
50 |
51 | ```
52 | pip install jupyter
53 | ```
54 |
55 | and then start the notebook server via
56 |
57 | ```
58 | jupyter notebook
59 | ```
60 |
61 |
62 | ## Documentation
63 | There is also an API documentation for libf0:
64 |
65 | https://groupmm.github.io/libf0
66 |
67 | ## Contributing
68 |
69 | We are happy for suggestions and contributions. We would be grateful for either directly contacting us via email (meinard.mueller@audiolabs-erlangen.de) or for creating an issue in our Github repository. Please do not submit a pull request without prior consultation with us.
70 |
71 | ## Tests
72 |
73 | We provide automated tests for each algorithm. To execute the test script, you will need to install extra requirements for testing:
74 |
75 | ```
76 | pip install 'libf0[tests]'
77 | pytest tests
78 | ```
79 |
80 | ## Licence
81 |
82 | The code for this toolbox is published under an MIT licence.
83 |
84 | ## Acknowledgements
85 |
86 | This work was supported by the German Research Foundation (MU 2686/13-1, SCHE 280/20-1). We thank Edgar Suárez and Vojtěch Pešek for helping with the implementations. Furthermore, we thank Fatemeh Eftekhar and Maryam Pirmoradi for testing the toolbox. The International Audio Laboratories Erlangen are a joint institution of the Friedrich-Alexander-Universität Erlangen-Nürnberg (FAU) and Fraunhofer Institute for Integrated Circuits IIS.
87 |
--------------------------------------------------------------------------------
/data/DCS_LI_QuartetB_Take03_S1_LRX_excerpt.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/data/DCS_LI_QuartetB_Take03_S1_LRX_excerpt.wav
--------------------------------------------------------------------------------
/demo_libf0.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "feeb7b4a",
6 | "metadata": {},
7 | "source": [
8 | "# libf0 - A Python Library for F0-Estimation in Music Recordings"
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": null,
14 | "id": "8a81c6dc",
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "import numpy as np\n",
19 | "import librosa\n",
20 | "from scipy.interpolate import interp1d\n",
21 | "\n",
22 | "import IPython.display as ipd\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "\n",
25 | "import libf0"
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "execution_count": null,
31 | "id": "82a6a26e-db62-41f0-9e8c-c718b1359752",
32 | "metadata": {},
33 | "outputs": [],
34 | "source": [
35 | "# Plot function\n",
36 | "def plot_f0_trajectory(Y_LF, t, f, f0, t_f0, figsize=(8.5, 3.4), xlim=(0, 11.5), ylim=(2000, 6000)):\n",
37 | " \"\"\"\n",
38 | " Plot a calculated f0 trajectory on the corresponding spectrogram\n",
39 | " \n",
40 | " Parameters\n",
41 | " ----------\n",
42 | " Y_LF : np.ndarray\n",
43 | " log-frequency spectrogram\n",
44 | " t : np.ndarray\n",
45 | " time axis of the spectrogram\n",
46 | " f : np.ndarray\n",
47 | " log-frequency axis of the spectrogram in cents\n",
48 | " f0 : np.ndarray\n",
49 | " f0 trajectory in cents\n",
50 | " t_f0 : np.ndarray\n",
51 | " time points of the f0 trajectory frames\n",
52 | " figsize : tuple\n",
53 | " figure size\n",
54 | " xlim : tuple\n",
55 | " x-limits\n",
56 | " ylim : tuple\n",
57 | " y-limits\n",
58 | " \"\"\"\n",
59 | " plt.figure(figsize=figsize)\n",
60 | "\n",
61 | " plt.imshow(Y_LF, cmap='gray_r', aspect='auto', origin='lower', extent=[t[0], t[-1], f[0], f[-1]])\n",
62 | " plt.plot(t_f0, f0, linestyle='', marker='.', markersize=5, color=[192/256, 0, 0])\n",
63 | "\n",
64 | " plt.xlim(xlim)\n",
65 | " plt.ylim(ylim)\n",
66 | "\n",
67 | " plt.gca().tick_params(axis='both', which='major', labelsize=10)\n",
68 | " plt.gca().tick_params(axis='both', which='minor', labelsize=10)\n",
69 | " \n",
70 | " plt.xlabel(\"Time (seconds)\", fontsize=12)\n",
71 | " plt.ylabel(\"Log-Frequency (cents)\", fontsize=12)\n",
72 | " \n",
73 | " cbar = plt.colorbar()\n",
74 | " cbar.ax.get_yaxis().labelpad = 15\n",
75 | " cbar.ax.set_ylabel('Log-Magnitude', rotation=270)\n",
76 | "\n",
77 | " plt.tight_layout()\n",
78 | " plt.show()"
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "id": "8f646465",
85 | "metadata": {},
86 | "outputs": [],
87 | "source": [
88 | "# load demo audio (a throat microphone recording of a soprano singer)\n",
89 | "fn_wav = \"./data/DCS_LI_QuartetB_Take03_S1_LRX_excerpt.wav\"\n",
90 | "x, Fs = librosa.load(fn_wav, sr=22050)\n",
91 | "ipd.display(ipd.Audio(x, rate=Fs, normalize=True)) # audio playback\n",
92 | "\n",
93 | "# shared parameters\n",
94 | "N = 2048 # window size in samples\n",
95 | "H = 256 # hop size in samples\n",
96 | "zero_pad = 2048 # zero-padding for STFT (only for visualization)\n",
97 | "F_min = 55.0 # minimum frequency of interest in Hz\n",
98 | "F_max = 1760.0 # maximum frequency of interest in Hz\n",
99 | "R = 10 # resolution of F0-estimations in cents\n",
100 | "\n",
101 | "# calculate magnitude spectrogram of input signal for visualization\n",
102 | "X = librosa.stft(x, n_fft=N+zero_pad, hop_length=H, win_length=N, window='hann', pad_mode='constant', center=True)\n",
103 | "Y = np.abs(X)\n",
104 | "F_coef_lin = librosa.fft_frequencies(sr=Fs, n_fft=N+zero_pad)\n",
105 | "T_coef = librosa.frames_to_time(np.arange(X.shape[1]), sr=Fs, hop_length=H)\n",
106 | "\n",
107 | "# interpolate magnitude spectrogram to a logarithmic frequency axis \n",
108 | "B = np.floor((1200 / R) * np.log2(F_max / F_min) + 0.5)\n",
109 | "F_coef_log_cents = np.arange(0, B) * R \n",
110 | "F_coef_log_hz = 2 ** (F_coef_log_cents / 1200) * F_min\n",
111 | "compute_Y_interpol = interp1d(F_coef_lin, Y, kind='cubic', axis=0)\n",
112 | "Y_LF = compute_Y_interpol(F_coef_log_hz)\n",
113 | "Y_LF[Y_LF < 0] = 0 # discard negative values after interpolation\n",
114 | "\n",
115 | "# use log-magnitude for visualizations\n",
116 | "Y_LF = np.log(1 + Y_LF)\n",
117 | "\n",
118 | "plot_f0_trajectory(Y_LF, T_coef, F_coef_log_cents, [], [])"
119 | ]
120 | },
121 | {
122 | "cell_type": "markdown",
123 | "id": "a2d8f9fe",
124 | "metadata": {},
125 | "source": [
126 | "### YIN\n",
127 | "\n",
128 | "For algorithmic details, see:\n",
129 | "\n",
130 | "Alain de Cheveigné and Hideki Kawahara. YIN, a fundamental frequency estimator for speech and music. Journal of the Acoustical Society of America (JASA), 111(4):1917–1930, 2002."
131 | ]
132 | },
133 | {
134 | "cell_type": "code",
135 | "execution_count": null,
136 | "id": "a9a165be",
137 | "metadata": {},
138 | "outputs": [],
139 | "source": [
140 | "# YIN parameters\n",
141 | "threshold = 0.15\n",
142 | "\n",
143 | "# run YIN algorithm\n",
144 | "f0_yin, t_yin, ap_yin = libf0.yin(x, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, threshold=threshold, verbose=True)\n",
145 | "\n",
146 | "# convert trajectory to cent scale\n",
147 | "f0_yin_cents = libf0.hz_to_cents(f0_yin, F_min)\n",
148 | "\n",
149 | "# plot the filtered result\n",
150 | "plot_f0_trajectory(Y_LF, T_coef, F_coef_log_cents, f0_yin_cents, t_yin)\n",
151 | "\n",
152 | "# sonify the filtered result (left: sonification, right: original audio)\n",
153 | "x_son_yin = libf0.sonify_trajectory_with_sinusoid(f0_yin, t_yin, len(x), Fs=Fs)\n",
154 | "ipd.display(ipd.Audio(np.vstack((x_son_yin.reshape(1, -1), x.reshape(1, -1))), rate=Fs, normalize=True))"
155 | ]
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "id": "f27baf76",
160 | "metadata": {},
161 | "source": [
162 | "### pYIN\n",
163 | "\n",
164 | "For algorithmic details, see:\n",
165 | "\n",
166 | "Matthias Mauch and Simon Dixon. pYIN: A fundamental frequency estimator using probabilistic threshold distributions. In IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), pages 659–663, Florence, Italy, 2014."
167 | ]
168 | },
169 | {
170 | "cell_type": "code",
171 | "execution_count": null,
172 | "id": "9986dfe1",
173 | "metadata": {},
174 | "outputs": [],
175 | "source": [
176 | "# set parameters\n",
177 | "thresholds = np.arange(0.01, 1, 0.01)\n",
178 | "R = 10 # bin resolution in cents\n",
179 | "\n",
180 | "# run pYIN algorithm\n",
181 | "f0_pyin, t_pyin, conf_pyin = libf0.pyin(x, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, R=R, thresholds=thresholds)\n",
182 | "\n",
183 | "# convert trajectory to cent scale\n",
184 | "f0_pyin_cents = libf0.hz_to_cents(f0_pyin, F_min)\n",
185 | "\n",
186 | "# plot the filtered result\n",
187 | "plot_f0_trajectory(Y_LF, T_coef, F_coef_log_cents, f0_pyin_cents, t_pyin)\n",
188 | "\n",
189 | "# sonify the filtered result (left: sonification, right: original audio)\n",
190 | "x_son_pyin = libf0.sonify_trajectory_with_sinusoid(f0_pyin, t_pyin, len(x), Fs=Fs)\n",
191 | "ipd.display(ipd.Audio(np.vstack((x_son_pyin.reshape(1, -1), x.reshape(1, -1))), rate=Fs, normalize=True))"
192 | ]
193 | },
194 | {
195 | "cell_type": "markdown",
196 | "id": "5218adb5-862b-4315-a85d-375dc69cc0e1",
197 | "metadata": {},
198 | "source": [
199 | "### Salience Algorithm\n",
200 | "\n",
201 | "For algorithmic details, see:\n",
202 | "\n",
203 | "Justin Salamon and Emilia Gómez. Melody extraction from polyphonic music signals using pitch contour characteristics. IEEE Transactions on Audio, Speech, and Language Processing, 20(6): 1759–1770, 2012.\n",
204 | "\n",
205 | "Meinard Müller. Fundamentals of Music Processing – Using Python and Jupyter Notebooks. Springer\n",
206 | "Verlag, 2nd edition, 2021. ISBN 978-3-030-69807-2. doi: 10.1007/978-3-030-69808-9."
207 | ]
208 | },
209 | {
210 | "cell_type": "code",
211 | "execution_count": null,
212 | "id": "b0f0edcb-1e8a-47e1-a40d-9a28dae69879",
213 | "metadata": {},
214 | "outputs": [],
215 | "source": [
216 | "# set parameters\n",
217 | "num_harm = 10 # number of harmonics for the summation\n",
218 | "freq_smoothing = 11 # length of the smoothing filter\n",
219 | "\n",
220 | "# run the salience algorithm\n",
221 | "f0_sal, t_sal, conf_sal = libf0.salience(x, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, R=R, num_harm=num_harm, freq_smooth_len=freq_smoothing)\n",
222 | "\n",
223 | "# convert trajectory to cent scale\n",
224 | "f0_sal_cents = libf0.hz_to_cents(f0_sal, F_min)\n",
225 | "\n",
226 | "# plot the result\n",
227 | "plot_f0_trajectory(Y_LF, T_coef, F_coef_log_cents, f0_sal_cents, t_sal)\n",
228 | "\n",
229 | "# sonify the result (left: sonification, right: original audio)\n",
230 | "x_son_sal = libf0.sonify_trajectory_with_sinusoid(f0_sal, t_sal, len(x), Fs=Fs)\n",
231 | "ipd.display(ipd.Audio(np.vstack((x_son_sal.reshape(1, -1), x.reshape(1, -1))), rate=Fs, normalize=True))"
232 | ]
233 | },
234 | {
235 | "cell_type": "markdown",
236 | "id": "0eabab3f",
237 | "metadata": {},
238 | "source": [
239 | "### SWIPE\n",
240 | "\n",
241 | "For algorithmic details, see:\n",
242 | "\n",
243 | "Arturo Camacho and John G. Harris. A sawtooth waveform inspired pitch estimator for speech and music. The Journal of the Acoustical Society of America, 124(3):1638–1652, 2008."
244 | ]
245 | },
246 | {
247 | "cell_type": "code",
248 | "execution_count": null,
249 | "id": "ee7a8ff4",
250 | "metadata": {},
251 | "outputs": [],
252 | "source": [
253 | "# set parameters\n",
254 | "threshold = 0.5 # confidence threshold between 0 and 1\n",
255 | "\n",
256 | "# run the SWIPE algorithm\n",
257 | "f0_swipe, t_swipe, conf_swipe = libf0.swipe(x, Fs, H, F_min, F_max, strength_threshold=threshold)\n",
258 | "\n",
259 | "# convert trajectory to cent scale\n",
260 | "f0_swipe_cents = libf0.hz_to_cents(f0_swipe, F_min)\n",
261 | "\n",
262 | "# plot the result\n",
263 | "plot_f0_trajectory(Y_LF, T_coef, F_coef_log_cents, f0_swipe_cents, t_swipe)\n",
264 | "\n",
265 | "# sonify the result (left: sonification, right: original audio)\n",
266 | "x_son_swipe = libf0.sonify_trajectory_with_sinusoid(f0_swipe, t_swipe, len(x), Fs=Fs)\n",
267 | "ipd.display(ipd.Audio(np.vstack((x_son_swipe.reshape(1, -1), x.reshape(1, -1))), rate=Fs, normalize=True))"
268 | ]
269 | },
270 | {
271 | "cell_type": "markdown",
272 | "id": "08b2121c",
273 | "metadata": {},
274 | "source": [
275 | "### SWIPE (slim)\n",
276 | "\n",
277 | "A more efficient and didactic implementation of the SWIPE algorithm."
278 | ]
279 | },
280 | {
281 | "cell_type": "code",
282 | "execution_count": null,
283 | "id": "bee19066",
284 | "metadata": {},
285 | "outputs": [],
286 | "source": [
287 | "# set parameters\n",
288 | "threshold = 0.5 # confidence threshold between 0 and 1\n",
289 | "\n",
290 | "# run the SWIPE algorithm\n",
291 | "f0_swipes, t_swipes, conf_swipes = libf0.swipe_slim(x, Fs, H, F_min, F_max, strength_threshold=threshold) # a simplified implementation\n",
292 | "\n",
293 | "# convert trajectory to cent scale\n",
294 | "f0_swipes_cents = libf0.hz_to_cents(f0_swipes, F_min)\n",
295 | "\n",
296 | "# plot the result\n",
297 | "plot_f0_trajectory(Y_LF, T_coef, F_coef_log_cents, f0_swipes_cents, t_swipe)\n",
298 | "\n",
299 | "# sonify the result (left: sonification, right: original audio)\n",
300 | "x_son_swipes = libf0.sonify_trajectory_with_sinusoid(f0_swipes, t_swipes, len(x), Fs=Fs)\n",
301 | "ipd.display(ipd.Audio(np.vstack((x_son_swipes.reshape(1, -1), x.reshape(1, -1))), rate=Fs, normalize=True))"
302 | ]
303 | },
304 | {
305 | "cell_type": "code",
306 | "execution_count": null,
307 | "id": "b271aa6d",
308 | "metadata": {},
309 | "outputs": [],
310 | "source": []
311 | }
312 | ],
313 | "metadata": {
314 | "kernelspec": {
315 | "display_name": "Python 3",
316 | "language": "python",
317 | "name": "python3"
318 | },
319 | "language_info": {
320 | "codemirror_mode": {
321 | "name": "ipython",
322 | "version": 3
323 | },
324 | "file_extension": ".py",
325 | "mimetype": "text/x-python",
326 | "name": "python",
327 | "nbconvert_exporter": "python",
328 | "pygments_lexer": "ipython3",
329 | "version": "3.8.10"
330 | }
331 | },
332 | "nbformat": 4,
333 | "nbformat_minor": 5
334 | }
335 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/build/html/.buildinfo:
--------------------------------------------------------------------------------
1 | # Sphinx build info version 1
2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3 | config: 8c91fd5622b39f1fe68671e5fb0fa0c2
4 | tags: 645f666f9bcd5a90fca523b33c5a78b7
5 |
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/environment.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/environment.pickle
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/getting_started.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/getting_started.doctree
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/index.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/index.doctree
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/index_pyin.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/index_pyin.doctree
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/index_salience.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/index_salience.doctree
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/index_swipe.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/index_swipe.doctree
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/index_swipe_slim.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/index_swipe_slim.doctree
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/index_utils.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/index_utils.doctree
--------------------------------------------------------------------------------
/docs/build/html/.doctrees/index_yin.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/.doctrees/index_yin.doctree
--------------------------------------------------------------------------------
/docs/build/html/_modules/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Overview: module code — libf0 1.0.2 documentation
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
58 |
59 |
60 |
61 |
62 | libf0
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Overview: module code
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
All modules for which code is available
80 |
87 |
88 |
89 |
90 |
104 |
105 |
106 |
107 |
108 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/getting_started.rst.txt:
--------------------------------------------------------------------------------
1 | Getting Started
2 | ===============
3 |
4 | Installation
5 | ------------
6 |
7 | With Python >= 3.6, you can install ``libf0`` using the Python package manager pip:
8 |
9 | .. code-block:: bash
10 |
11 | pip install libf0
12 |
13 | Contributing
14 | ------------
15 |
16 | For development, testing, or generating the API documentation, clone the git repository and install:
17 |
18 | .. code-block:: bash
19 |
20 | git clone https://github.com/groupmm/libf0.git
21 | cd libf0
22 | pip install -e .[dev,tests,docs]
23 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index.rst.txt:
--------------------------------------------------------------------------------
1 | .. libf0 documentation master file, created by
2 | sphinx-quickstart on Thu May 27 14:48:45 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | libf0
7 | ==================================
8 | ``libf0`` is a Python toolbox for fundamental frequency (F0) estimation in music recordings.
9 |
10 | If you use this toolbox, please cite:
11 |
12 | .. [#] Sebastian Rosenzweig, Simon Schwär, and Meinard Müller. libf0: A Python Library for Fundamental Frequency Estimation. In Late Breaking Demos of the International Society for Music Information Retrieval Conference (ISMIR), Bengaluru, India, 2022.
13 |
14 |
15 | To reference the original algorithms implemented in this toolbox, please cite:
16 |
17 | .. [#] Alain De Cheveigné and Hideki Kawahara: YIN, a fundamental frequency estimator for speech and music, The Journal of the Acoustical Society of America 111.4 (2002): 1917-1930.
18 | .. [#] Matthias Mauch and Simon Dixon: PYIN: A fundamental frequency estimator using probabilistic threshold distributions, IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP) (2014): 659-663.
19 | .. [#] Justin Salamon and Emilia Gómez: Melody Extraction From Polyphonic Music Signals Using Pitch Contour Characteristics, IEEE Transactions on Audio, Speech, and Language Processing, vol. 20, no. 6, pp. 1759–1770, 2012.
20 | .. [#] Arturo Camacho and John G. Harris: A sawtooth waveform inspired pitch estimator for speech and music. The Journal of the Acoustical Society of America, vol. 124, no. 3, pp. 1638–1652, 2008
21 | .. [#] Meinard Müller. Fundamentals of Music Processing – Using Python and Jupyter Notebooks. Springer Verlag, 2nd edition, 2021. ISBN 978-3-030-69807-2. doi: 10.1007/978-3-030-69808-9.
22 |
23 |
24 | .. toctree::
25 | :hidden:
26 |
27 | getting_started
28 |
29 |
30 |
31 | .. toctree::
32 | :caption: API Documentation
33 | :maxdepth: 1
34 | :hidden:
35 |
36 | index_yin
37 | index_pyin
38 | index_salience
39 | index_swipe
40 | index_swipe_slim
41 | index_utils
42 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index_pyin.rst.txt:
--------------------------------------------------------------------------------
1 | pyin
2 | ==============
3 |
4 | .. automodule:: libf0.pyin
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index_salience.rst.txt:
--------------------------------------------------------------------------------
1 | salience
2 | ==============
3 |
4 | .. automodule:: libf0.salience
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index_swipe.rst.txt:
--------------------------------------------------------------------------------
1 | swipe
2 | ==============
3 |
4 | .. automodule:: libf0.swipe
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index_swipe_slim.rst.txt:
--------------------------------------------------------------------------------
1 | swipe_slim
2 | ==============
3 |
4 | .. automodule:: libf0.swipe_slim
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index_utils.rst.txt:
--------------------------------------------------------------------------------
1 | utils
2 | =====
3 |
4 | .. automodule:: libf0.utils
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index_yin.rst.txt:
--------------------------------------------------------------------------------
1 | yin
2 | ==============
3 |
4 | .. automodule:: libf0.yin
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/build/html/_static/_sphinx_javascript_frameworks_compat.js:
--------------------------------------------------------------------------------
1 | /*
2 | * _sphinx_javascript_frameworks_compat.js
3 | * ~~~~~~~~~~
4 | *
5 | * Compatability shim for jQuery and underscores.js.
6 | *
7 | * WILL BE REMOVED IN Sphinx 6.0
8 | * xref RemovedInSphinx60Warning
9 | *
10 | */
11 |
12 | /**
13 | * select a different prefix for underscore
14 | */
15 | $u = _.noConflict();
16 |
17 |
18 | /**
19 | * small helper function to urldecode strings
20 | *
21 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
22 | */
23 | jQuery.urldecode = function(x) {
24 | if (!x) {
25 | return x
26 | }
27 | return decodeURIComponent(x.replace(/\+/g, ' '));
28 | };
29 |
30 | /**
31 | * small helper function to urlencode strings
32 | */
33 | jQuery.urlencode = encodeURIComponent;
34 |
35 | /**
36 | * This function returns the parsed url parameters of the
37 | * current request. Multiple values per key are supported,
38 | * it will always return arrays of strings for the value parts.
39 | */
40 | jQuery.getQueryParameters = function(s) {
41 | if (typeof s === 'undefined')
42 | s = document.location.search;
43 | var parts = s.substr(s.indexOf('?') + 1).split('&');
44 | var result = {};
45 | for (var i = 0; i < parts.length; i++) {
46 | var tmp = parts[i].split('=', 2);
47 | var key = jQuery.urldecode(tmp[0]);
48 | var value = jQuery.urldecode(tmp[1]);
49 | if (key in result)
50 | result[key].push(value);
51 | else
52 | result[key] = [value];
53 | }
54 | return result;
55 | };
56 |
57 | /**
58 | * highlight a given string on a jquery object by wrapping it in
59 | * span elements with the given class name.
60 | */
61 | jQuery.fn.highlightText = function(text, className) {
62 | function highlight(node, addItems) {
63 | if (node.nodeType === 3) {
64 | var val = node.nodeValue;
65 | var pos = val.toLowerCase().indexOf(text);
66 | if (pos >= 0 &&
67 | !jQuery(node.parentNode).hasClass(className) &&
68 | !jQuery(node.parentNode).hasClass("nohighlight")) {
69 | var span;
70 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
71 | if (isInSVG) {
72 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
73 | } else {
74 | span = document.createElement("span");
75 | span.className = className;
76 | }
77 | span.appendChild(document.createTextNode(val.substr(pos, text.length)));
78 | node.parentNode.insertBefore(span, node.parentNode.insertBefore(
79 | document.createTextNode(val.substr(pos + text.length)),
80 | node.nextSibling));
81 | node.nodeValue = val.substr(0, pos);
82 | if (isInSVG) {
83 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
84 | var bbox = node.parentElement.getBBox();
85 | rect.x.baseVal.value = bbox.x;
86 | rect.y.baseVal.value = bbox.y;
87 | rect.width.baseVal.value = bbox.width;
88 | rect.height.baseVal.value = bbox.height;
89 | rect.setAttribute('class', className);
90 | addItems.push({
91 | "parent": node.parentNode,
92 | "target": rect});
93 | }
94 | }
95 | }
96 | else if (!jQuery(node).is("button, select, textarea")) {
97 | jQuery.each(node.childNodes, function() {
98 | highlight(this, addItems);
99 | });
100 | }
101 | }
102 | var addItems = [];
103 | var result = this.each(function() {
104 | highlight(this, addItems);
105 | });
106 | for (var i = 0; i < addItems.length; ++i) {
107 | jQuery(addItems[i].parent).before(addItems[i].target);
108 | }
109 | return result;
110 | };
111 |
112 | /*
113 | * backward compatibility for jQuery.browser
114 | * This will be supported until firefox bug is fixed.
115 | */
116 | if (!jQuery.browser) {
117 | jQuery.uaMatch = function(ua) {
118 | ua = ua.toLowerCase();
119 |
120 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
121 | /(webkit)[ \/]([\w.]+)/.exec(ua) ||
122 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
123 | /(msie) ([\w.]+)/.exec(ua) ||
124 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
125 | [];
126 |
127 | return {
128 | browser: match[ 1 ] || "",
129 | version: match[ 2 ] || "0"
130 | };
131 | };
132 | jQuery.browser = {};
133 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
134 | }
135 |
--------------------------------------------------------------------------------
/docs/build/html/_static/css/badge_only.css:
--------------------------------------------------------------------------------
1 | .clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-bold-italic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-bold-italic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-normal-italic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-normal-italic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-normal.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/css/fonts/lato-normal.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/doctools.js:
--------------------------------------------------------------------------------
1 | /*
2 | * doctools.js
3 | * ~~~~~~~~~~~
4 | *
5 | * Base JavaScript utilities for all Sphinx HTML documentation.
6 | *
7 | * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
8 | * :license: BSD, see LICENSE for details.
9 | *
10 | */
11 | "use strict";
12 |
13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
14 | "TEXTAREA",
15 | "INPUT",
16 | "SELECT",
17 | "BUTTON",
18 | ]);
19 |
20 | const _ready = (callback) => {
21 | if (document.readyState !== "loading") {
22 | callback();
23 | } else {
24 | document.addEventListener("DOMContentLoaded", callback);
25 | }
26 | };
27 |
28 | /**
29 | * Small JavaScript module for the documentation.
30 | */
31 | const Documentation = {
32 | init: () => {
33 | Documentation.initDomainIndexTable();
34 | Documentation.initOnKeyListeners();
35 | },
36 |
37 | /**
38 | * i18n support
39 | */
40 | TRANSLATIONS: {},
41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
42 | LOCALE: "unknown",
43 |
44 | // gettext and ngettext don't access this so that the functions
45 | // can safely bound to a different name (_ = Documentation.gettext)
46 | gettext: (string) => {
47 | const translated = Documentation.TRANSLATIONS[string];
48 | switch (typeof translated) {
49 | case "undefined":
50 | return string; // no translation
51 | case "string":
52 | return translated; // translation exists
53 | default:
54 | return translated[0]; // (singular, plural) translation tuple exists
55 | }
56 | },
57 |
58 | ngettext: (singular, plural, n) => {
59 | const translated = Documentation.TRANSLATIONS[singular];
60 | if (typeof translated !== "undefined")
61 | return translated[Documentation.PLURAL_EXPR(n)];
62 | return n === 1 ? singular : plural;
63 | },
64 |
65 | addTranslations: (catalog) => {
66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages);
67 | Documentation.PLURAL_EXPR = new Function(
68 | "n",
69 | `return (${catalog.plural_expr})`
70 | );
71 | Documentation.LOCALE = catalog.locale;
72 | },
73 |
74 | /**
75 | * helper function to focus on search bar
76 | */
77 | focusSearchBar: () => {
78 | document.querySelectorAll("input[name=q]")[0]?.focus();
79 | },
80 |
81 | /**
82 | * Initialise the domain index toggle buttons
83 | */
84 | initDomainIndexTable: () => {
85 | const toggler = (el) => {
86 | const idNumber = el.id.substr(7);
87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
88 | if (el.src.substr(-9) === "minus.png") {
89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
90 | toggledRows.forEach((el) => (el.style.display = "none"));
91 | } else {
92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
93 | toggledRows.forEach((el) => (el.style.display = ""));
94 | }
95 | };
96 |
97 | const togglerElements = document.querySelectorAll("img.toggler");
98 | togglerElements.forEach((el) =>
99 | el.addEventListener("click", (event) => toggler(event.currentTarget))
100 | );
101 | togglerElements.forEach((el) => (el.style.display = ""));
102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
103 | },
104 |
105 | initOnKeyListeners: () => {
106 | // only install a listener if it is really needed
107 | if (
108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
110 | )
111 | return;
112 |
113 | document.addEventListener("keydown", (event) => {
114 | // bail for input elements
115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
116 | // bail with special keys
117 | if (event.altKey || event.ctrlKey || event.metaKey) return;
118 |
119 | if (!event.shiftKey) {
120 | switch (event.key) {
121 | case "ArrowLeft":
122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
123 |
124 | const prevLink = document.querySelector('link[rel="prev"]');
125 | if (prevLink && prevLink.href) {
126 | window.location.href = prevLink.href;
127 | event.preventDefault();
128 | }
129 | break;
130 | case "ArrowRight":
131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
132 |
133 | const nextLink = document.querySelector('link[rel="next"]');
134 | if (nextLink && nextLink.href) {
135 | window.location.href = nextLink.href;
136 | event.preventDefault();
137 | }
138 | break;
139 | }
140 | }
141 |
142 | // some keyboard layouts may need Shift to get /
143 | switch (event.key) {
144 | case "/":
145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
146 | Documentation.focusSearchBar();
147 | event.preventDefault();
148 | }
149 | });
150 | },
151 | };
152 |
153 | // quick alias for translations
154 | const _ = Documentation.gettext;
155 |
156 | _ready(Documentation.init);
157 |
--------------------------------------------------------------------------------
/docs/build/html/_static/documentation_options.js:
--------------------------------------------------------------------------------
1 | var DOCUMENTATION_OPTIONS = {
2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
3 | VERSION: '1.0.2',
4 | LANGUAGE: 'en',
5 | COLLAPSE_INDEX: false,
6 | BUILDER: 'html',
7 | FILE_SUFFIX: '.html',
8 | LINK_SUFFIX: '.html',
9 | HAS_SOURCE: true,
10 | SOURCELINK_SUFFIX: '.txt',
11 | NAVIGATION_WITH_KEYS: false,
12 | SHOW_SEARCH_SUMMARY: true,
13 | ENABLE_SEARCH_SHORTCUTS: true,
14 | };
--------------------------------------------------------------------------------
/docs/build/html/_static/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/file.png
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bold.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bold.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bolditalic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bolditalic.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bolditalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bolditalic.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bolditalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bolditalic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-bolditalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-bolditalic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-italic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-italic.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-italic.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-italic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-italic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-regular.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-regular.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-regular.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Lato/lato-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Lato/lato-regular.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Light.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Light.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Regular.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Regular.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Thin.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/Roboto-Slab-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/Roboto-Slab-Thin.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-bold-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-bold-italic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-bold-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-bold-italic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-normal-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-normal-italic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-normal-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-normal-italic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-normal.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/fonts/lato-normal.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/fonts/lato-normal.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/js/badge_only.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
--------------------------------------------------------------------------------
/docs/build/html/_static/js/html5shiv-printshiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML=" ",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document);
--------------------------------------------------------------------------------
/docs/build/html/_static/js/html5shiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML=" ",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
--------------------------------------------------------------------------------
/docs/build/html/_static/js/modernizr.min.js:
--------------------------------------------------------------------------------
1 | /* Modernizr 2.6.2 (Custom Build) | MIT & BSD
2 | * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load
3 | */
4 | ;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a){var e=a[d];if(!G(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function K(){e.input=function(c){for(var d=0,e=c.length;d',a,""].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML=" ",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f"),n("table.docutils.footnote").wrap(""),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(' '),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0
63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
65 | var s_v = "^(" + C + ")?" + v; // vowel in stem
66 |
67 | this.stemWord = function (w) {
68 | var stem;
69 | var suffix;
70 | var firstch;
71 | var origword = w;
72 |
73 | if (w.length < 3)
74 | return w;
75 |
76 | var re;
77 | var re2;
78 | var re3;
79 | var re4;
80 |
81 | firstch = w.substr(0,1);
82 | if (firstch == "y")
83 | w = firstch.toUpperCase() + w.substr(1);
84 |
85 | // Step 1a
86 | re = /^(.+?)(ss|i)es$/;
87 | re2 = /^(.+?)([^s])s$/;
88 |
89 | if (re.test(w))
90 | w = w.replace(re,"$1$2");
91 | else if (re2.test(w))
92 | w = w.replace(re2,"$1$2");
93 |
94 | // Step 1b
95 | re = /^(.+?)eed$/;
96 | re2 = /^(.+?)(ed|ing)$/;
97 | if (re.test(w)) {
98 | var fp = re.exec(w);
99 | re = new RegExp(mgr0);
100 | if (re.test(fp[1])) {
101 | re = /.$/;
102 | w = w.replace(re,"");
103 | }
104 | }
105 | else if (re2.test(w)) {
106 | var fp = re2.exec(w);
107 | stem = fp[1];
108 | re2 = new RegExp(s_v);
109 | if (re2.test(stem)) {
110 | w = stem;
111 | re2 = /(at|bl|iz)$/;
112 | re3 = new RegExp("([^aeiouylsz])\\1$");
113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
114 | if (re2.test(w))
115 | w = w + "e";
116 | else if (re3.test(w)) {
117 | re = /.$/;
118 | w = w.replace(re,"");
119 | }
120 | else if (re4.test(w))
121 | w = w + "e";
122 | }
123 | }
124 |
125 | // Step 1c
126 | re = /^(.+?)y$/;
127 | if (re.test(w)) {
128 | var fp = re.exec(w);
129 | stem = fp[1];
130 | re = new RegExp(s_v);
131 | if (re.test(stem))
132 | w = stem + "i";
133 | }
134 |
135 | // Step 2
136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
137 | if (re.test(w)) {
138 | var fp = re.exec(w);
139 | stem = fp[1];
140 | suffix = fp[2];
141 | re = new RegExp(mgr0);
142 | if (re.test(stem))
143 | w = stem + step2list[suffix];
144 | }
145 |
146 | // Step 3
147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
148 | if (re.test(w)) {
149 | var fp = re.exec(w);
150 | stem = fp[1];
151 | suffix = fp[2];
152 | re = new RegExp(mgr0);
153 | if (re.test(stem))
154 | w = stem + step3list[suffix];
155 | }
156 |
157 | // Step 4
158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
159 | re2 = /^(.+?)(s|t)(ion)$/;
160 | if (re.test(w)) {
161 | var fp = re.exec(w);
162 | stem = fp[1];
163 | re = new RegExp(mgr1);
164 | if (re.test(stem))
165 | w = stem;
166 | }
167 | else if (re2.test(w)) {
168 | var fp = re2.exec(w);
169 | stem = fp[1] + fp[2];
170 | re2 = new RegExp(mgr1);
171 | if (re2.test(stem))
172 | w = stem;
173 | }
174 |
175 | // Step 5
176 | re = /^(.+?)e$/;
177 | if (re.test(w)) {
178 | var fp = re.exec(w);
179 | stem = fp[1];
180 | re = new RegExp(mgr1);
181 | re2 = new RegExp(meq1);
182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
184 | w = stem;
185 | }
186 | re = /ll$/;
187 | re2 = new RegExp(mgr1);
188 | if (re.test(w) && re2.test(w)) {
189 | re = /.$/;
190 | w = w.replace(re,"");
191 | }
192 |
193 | // and turn initial Y back to y
194 | if (firstch == "y")
195 | w = firstch.toLowerCase() + w.substr(1);
196 | return w;
197 | }
198 | }
199 |
200 |
--------------------------------------------------------------------------------
/docs/build/html/_static/libf0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/libf0.png
--------------------------------------------------------------------------------
/docs/build/html/_static/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/minus.png
--------------------------------------------------------------------------------
/docs/build/html/_static/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/_static/plus.png
--------------------------------------------------------------------------------
/docs/build/html/_static/pygments.css:
--------------------------------------------------------------------------------
1 | pre { line-height: 125%; }
2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
6 | .highlight .hll { background-color: #ffffcc }
7 | .highlight { background: #eeffcc; }
8 | .highlight .c { color: #408090; font-style: italic } /* Comment */
9 | .highlight .err { border: 1px solid #FF0000 } /* Error */
10 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */
11 | .highlight .o { color: #666666 } /* Operator */
12 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
13 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
14 | .highlight .cp { color: #007020 } /* Comment.Preproc */
15 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */
16 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
17 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */
19 | .highlight .ge { font-style: italic } /* Generic.Emph */
20 | .highlight .gr { color: #FF0000 } /* Generic.Error */
21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
22 | .highlight .gi { color: #00A000 } /* Generic.Inserted */
23 | .highlight .go { color: #333333 } /* Generic.Output */
24 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
25 | .highlight .gs { font-weight: bold } /* Generic.Strong */
26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */
28 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
29 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
30 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
31 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */
32 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
33 | .highlight .kt { color: #902000 } /* Keyword.Type */
34 | .highlight .m { color: #208050 } /* Literal.Number */
35 | .highlight .s { color: #4070a0 } /* Literal.String */
36 | .highlight .na { color: #4070a0 } /* Name.Attribute */
37 | .highlight .nb { color: #007020 } /* Name.Builtin */
38 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
39 | .highlight .no { color: #60add5 } /* Name.Constant */
40 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
41 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
42 | .highlight .ne { color: #007020 } /* Name.Exception */
43 | .highlight .nf { color: #06287e } /* Name.Function */
44 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
45 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
46 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
47 | .highlight .nv { color: #bb60d5 } /* Name.Variable */
48 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */
50 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */
51 | .highlight .mf { color: #208050 } /* Literal.Number.Float */
52 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */
53 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */
54 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */
55 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */
56 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
57 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */
58 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */
59 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
60 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */
61 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
62 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
63 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
64 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */
65 | .highlight .sr { color: #235388 } /* Literal.String.Regex */
66 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */
67 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */
68 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
69 | .highlight .fm { color: #06287e } /* Name.Function.Magic */
70 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
71 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
72 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
73 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */
74 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
--------------------------------------------------------------------------------
/docs/build/html/_static/sphinx_highlight.js:
--------------------------------------------------------------------------------
1 | /* Highlighting utilities for Sphinx HTML documentation. */
2 | "use strict";
3 |
4 | const SPHINX_HIGHLIGHT_ENABLED = true
5 |
6 | /**
7 | * highlight a given string on a node by wrapping it in
8 | * span elements with the given class name.
9 | */
10 | const _highlight = (node, addItems, text, className) => {
11 | if (node.nodeType === Node.TEXT_NODE) {
12 | const val = node.nodeValue;
13 | const parent = node.parentNode;
14 | const pos = val.toLowerCase().indexOf(text);
15 | if (
16 | pos >= 0 &&
17 | !parent.classList.contains(className) &&
18 | !parent.classList.contains("nohighlight")
19 | ) {
20 | let span;
21 |
22 | const closestNode = parent.closest("body, svg, foreignObject");
23 | const isInSVG = closestNode && closestNode.matches("svg");
24 | if (isInSVG) {
25 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
26 | } else {
27 | span = document.createElement("span");
28 | span.classList.add(className);
29 | }
30 |
31 | span.appendChild(document.createTextNode(val.substr(pos, text.length)));
32 | parent.insertBefore(
33 | span,
34 | parent.insertBefore(
35 | document.createTextNode(val.substr(pos + text.length)),
36 | node.nextSibling
37 | )
38 | );
39 | node.nodeValue = val.substr(0, pos);
40 |
41 | if (isInSVG) {
42 | const rect = document.createElementNS(
43 | "http://www.w3.org/2000/svg",
44 | "rect"
45 | );
46 | const bbox = parent.getBBox();
47 | rect.x.baseVal.value = bbox.x;
48 | rect.y.baseVal.value = bbox.y;
49 | rect.width.baseVal.value = bbox.width;
50 | rect.height.baseVal.value = bbox.height;
51 | rect.setAttribute("class", className);
52 | addItems.push({ parent: parent, target: rect });
53 | }
54 | }
55 | } else if (node.matches && !node.matches("button, select, textarea")) {
56 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
57 | }
58 | };
59 | const _highlightText = (thisNode, text, className) => {
60 | let addItems = [];
61 | _highlight(thisNode, addItems, text, className);
62 | addItems.forEach((obj) =>
63 | obj.parent.insertAdjacentElement("beforebegin", obj.target)
64 | );
65 | };
66 |
67 | /**
68 | * Small JavaScript module for the documentation.
69 | */
70 | const SphinxHighlight = {
71 |
72 | /**
73 | * highlight the search words provided in localstorage in the text
74 | */
75 | highlightSearchWords: () => {
76 | if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
77 |
78 | // get and clear terms from localstorage
79 | const url = new URL(window.location);
80 | const highlight =
81 | localStorage.getItem("sphinx_highlight_terms")
82 | || url.searchParams.get("highlight")
83 | || "";
84 | localStorage.removeItem("sphinx_highlight_terms")
85 | url.searchParams.delete("highlight");
86 | window.history.replaceState({}, "", url);
87 |
88 | // get individual terms from highlight string
89 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
90 | if (terms.length === 0) return; // nothing to do
91 |
92 | // There should never be more than one element matching "div.body"
93 | const divBody = document.querySelectorAll("div.body");
94 | const body = divBody.length ? divBody[0] : document.querySelector("body");
95 | window.setTimeout(() => {
96 | terms.forEach((term) => _highlightText(body, term, "highlighted"));
97 | }, 10);
98 |
99 | const searchBox = document.getElementById("searchbox");
100 | if (searchBox === null) return;
101 | searchBox.appendChild(
102 | document
103 | .createRange()
104 | .createContextualFragment(
105 | '' +
106 | '' +
107 | _("Hide Search Matches") +
108 | "
"
109 | )
110 | );
111 | },
112 |
113 | /**
114 | * helper function to hide the search marks again
115 | */
116 | hideSearchWords: () => {
117 | document
118 | .querySelectorAll("#searchbox .highlight-link")
119 | .forEach((el) => el.remove());
120 | document
121 | .querySelectorAll("span.highlighted")
122 | .forEach((el) => el.classList.remove("highlighted"));
123 | localStorage.removeItem("sphinx_highlight_terms")
124 | },
125 |
126 | initEscapeListener: () => {
127 | // only install a listener if it is really needed
128 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
129 |
130 | document.addEventListener("keydown", (event) => {
131 | // bail for input elements
132 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
133 | // bail with special keys
134 | if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
135 | if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
136 | SphinxHighlight.hideSearchWords();
137 | event.preventDefault();
138 | }
139 | });
140 | },
141 | };
142 |
143 | _ready(SphinxHighlight.highlightSearchWords);
144 | _ready(SphinxHighlight.initEscapeListener);
145 |
--------------------------------------------------------------------------------
/docs/build/html/getting_started.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Getting Started — libf0 1.0.2 documentation
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
65 |
66 |
67 |
68 |
69 | libf0
70 |
71 |
72 |
73 |
74 |
84 |
85 |
86 |
87 |
88 | Getting Started
89 |
90 | Installation
91 | With Python >= 3.6, you can install libf0
using the Python package manager pip:
92 |
95 |
96 |
97 | Contributing
98 | For development, testing, or generating the API documentation, clone the git repository and install:
99 | git clone https://github.com/groupmm/libf0.git
100 | cd libf0
101 | pip install -e .[ dev,tests,docs]
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
127 |
128 |
129 |
130 |
131 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/docs/build/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | libf0 — libf0 1.0.2 documentation
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
60 |
61 |
62 |
63 |
64 | libf0
65 |
66 |
67 |
68 |
69 |
79 |
80 |
81 |
82 |
83 | libf0
84 | libf0
is a Python toolbox for fundamental frequency (F0) estimation in music recordings.
85 | If you use this toolbox, please cite:
86 |
91 | To reference the original algorithms implemented in this toolbox, please cite:
92 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
134 |
135 |
136 |
137 |
138 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/docs/build/html/index_swipe_slim.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | swipe_slim — libf0 1.0.2 documentation
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
66 |
67 |
68 |
69 |
70 | libf0
71 |
72 |
73 |
74 |
75 |
85 |
86 |
87 |
88 |
89 | swipe_slim
90 |
91 |
Description: libf0 SWIPE slim implementation
92 |
Contributors: Sebastian Rosenzweig, Simon Schwär, Meinard Müller
93 |
94 |
This file is part of libf0.
95 |
96 |
97 |
98 | libf0.swipe_slim. swipe_slim ( x , Fs = 22050 , H = 256 , F_min = 55.0 , F_max = 1760.0 , R = 10 , strength_threshold = 0 ) [source]
99 | Slim and didactical implementation of a sawtooth waveform inspired pitch estimator (SWIPE).
100 | This version uses a log-frequency spectrogram instead of ERB filters. Furthermore, it is implemented more
101 | efficiently. See swipe() for the original implementation.
102 |
109 |
110 | Parameters
111 |
112 | x (ndarray ) – Audio signal
113 | Fs (int ) – Sampling rate
114 | H (int ) – Hop size
115 | F_min (float or int ) – Minimal frequency
116 | F_max (float or int ) – Maximal frequency
117 | R (float ) – resolution of the pitch candidate bins in cents (default = 10)
118 | strength_threshold (float ) – confidence threshold [0, 1] for the pitch detection (default value = 0)
119 |
120 |
121 | Returns
122 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | libf0.swipe_slim. compute_kernel ( f , F_coef_log_hz ) [source]
135 | Compute a SWIPE’ kernel.
136 |
137 | Parameters
138 |
142 |
143 | Returns
144 | k – Kernel
145 |
146 | Return type
147 | ndarray
148 |
149 |
150 |
151 |
152 |
153 |
154 | libf0.swipe_slim. prime_and_one ( upto = 1000000 ) [source]
155 | Returns a set of prime numbers, adapted from http://rebrained.com/?p=458
156 |
157 | Parameters
158 | upto (int ) – Find prime numbers up to this number
159 |
160 | Return type
161 | A set of prime numbers including 1 & 2
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
188 |
189 |
190 |
191 |
192 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/docs/build/html/index_utils.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | utils — libf0 1.0.2 documentation
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
65 |
66 |
67 |
68 |
69 | libf0
70 |
71 |
72 |
73 |
74 |
84 |
85 |
86 |
87 |
88 | utils
89 |
90 |
Description: libf0 utility functions
91 |
Contributors: Sebastian Rosenzweig, Simon Schwär, Meinard Müller
92 |
93 |
This file is part of libf0.
94 |
95 |
96 |
97 | libf0.utils. sonify_trajectory_with_sinusoid ( f0 , t , audio_len , confidence = None , Fs = 22050 , smooth_len = 11 ) [source]
98 | Sonification of trajectory with sinusoidal. Adapted from FMP notebook: C8/C8S2_FundFreqTracking.ipynb
99 |
100 | Parameters
101 |
102 | f0 (ndarray ) – F0-trajectory
103 | t (ndarray ) – Time axis
104 | audio_len (int ) – Desired audio length in samples
105 | confidence (None or ndarray ) – Confidence values for amplitude control
106 | Fs (int ) – Sampling rate
107 | smooth_len (int ) – Smoothing filter length to avoid clicks in the sonification
108 |
109 |
110 | Returns
111 | x_soni – Sonified F0-trajectory
112 |
113 | Return type
114 | ndarray
115 |
116 |
117 |
118 |
119 |
120 |
121 | libf0.utils. hz_to_cents ( F , F_ref = 55.0 ) [source]
122 | Converts frequency in Hz to cents.
123 |
124 | Parameters
125 |
129 |
130 | Returns
131 | F_cents – Frequency in cents
132 |
133 | Return type
134 | float or ndarray
135 |
136 |
137 |
138 |
139 |
140 |
141 | libf0.utils. cents_to_hz ( F_cents , F_ref = 55.0 ) [source]
142 | Converts frequency in cents to Hz.
143 |
144 | Parameters
145 |
149 |
150 | Returns
151 | F – Frequency in Hz
152 |
153 | Return type
154 | float or ndarray
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
180 |
181 |
182 |
183 |
184 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/docs/build/html/objects.inv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/build/html/objects.inv
--------------------------------------------------------------------------------
/docs/build/html/py-modindex.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Python Module Index — libf0 1.0.2 documentation
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
61 |
62 |
63 |
64 |
65 | libf0
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Python Module Index
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
Python Module Index
84 |
85 |
88 |
89 |
130 |
131 |
132 |
133 |
134 |
148 |
149 |
150 |
151 |
152 |
157 |
158 |
159 |
--------------------------------------------------------------------------------
/docs/build/html/search.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Search — libf0 1.0.2 documentation
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
61 |
62 |
63 |
64 |
65 | libf0
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Search
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | Please activate JavaScript to enable the search functionality.
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
111 |
112 |
113 |
114 |
115 |
120 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/source/_static/libf0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/docs/source/_static/libf0.png
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import re
15 | import sys
16 |
17 | DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
18 | assert os.path.exists(os.path.join(DIR, 'libf0'))
19 | sys.path.insert(0, DIR)
20 |
21 | # -- Project information -----------------------------------------------------
22 |
23 | project = 'libf0'
24 | copyright = '2022, Sebastian Rosenzweig, Simon Schwär, Meinard Müller'
25 | author = 'Sebastian Rosenzweig, Simon Schwär, Meinard Müller'
26 |
27 | #reading version from local setup file
28 | setup_fn = next(fn for fn in ['setup.py'] if os.path.exists(os.path.join(DIR, fn)))
29 | with open(os.path.join(DIR, setup_fn), 'r') as stream:
30 | setup_content = stream.read()
31 | version_match = re.search("version='(.*?)'", setup_content)
32 | assert version_match is not None
33 | libtsm_version = version_match.group(1)
34 |
35 | version = libtsm_version
36 | release = libtsm_version
37 |
38 | # -- General configuration ---------------------------------------------------
39 |
40 | # Add any Sphinx extension module names here, as strings. They can be
41 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
42 | # ones.
43 | extensions = [
44 | 'sphinx.ext.autodoc', # documentation based on docstrings
45 | 'sphinx.ext.napoleon', # for having google/numpy style docstrings
46 | 'sphinx.ext.viewcode', # link source code
47 | 'sphinx.ext.intersphinx',
48 | 'sphinx.ext.autosummary',
49 | 'sphinx.ext.extlinks'
50 | ]
51 |
52 | # Add any paths that contain templates here, relative to this directory.
53 | templates_path = ['_templates']
54 |
55 | # List of patterns, relative to source directory, that match files and
56 | # directories to ignore when looking for source files.
57 | # This pattern also affects html_static_path and html_extra_path.
58 | exclude_patterns = []
59 |
60 | # The name of the Pygments (syntax highlighting) style to use.
61 | pygments_style = 'sphinx'
62 |
63 | autodoc_member_order = 'bysource' # avoid sorting functions alpha
64 |
65 | # -- Options for HTML output -------------------------------------------------
66 |
67 | # The theme to use for HTML and HTML Help pages. See the documentation for
68 | # a list of builtin themes.
69 | #
70 | import sphinx_rtd_theme # noqa
71 |
72 | html_theme = "sphinx_rtd_theme"
73 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
74 |
75 | # Add any paths that contain custom static files (such as style sheets) here,
76 | # relative to this directory. They are copied after the builtin static files,
77 | # so a file named "default.css" will overwrite the builtin "default.css".
78 | html_static_path = ['_static']
79 |
80 | html_use_index = True
81 | html_use_modindex = True
82 |
83 | html_logo = os.path.join(html_static_path[0], 'libf0.png')
84 |
85 | html_theme_options = {'logo_only': True}
86 |
--------------------------------------------------------------------------------
/docs/source/getting_started.rst:
--------------------------------------------------------------------------------
1 | Getting Started
2 | ===============
3 |
4 | Installation
5 | ------------
6 |
7 | With Python >= 3.6, you can install ``libf0`` using the Python package manager pip:
8 |
9 | .. code-block:: bash
10 |
11 | pip install libf0
12 |
13 | Contributing
14 | ------------
15 |
16 | For development, testing, or generating the API documentation, clone the git repository and install:
17 |
18 | .. code-block:: bash
19 |
20 | git clone https://github.com/groupmm/libf0.git
21 | cd libf0
22 | pip install -e .[dev,tests,docs]
23 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. libf0 documentation master file, created by
2 | sphinx-quickstart on Thu May 27 14:48:45 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | libf0
7 | ==================================
8 | ``libf0`` is a Python toolbox for fundamental frequency (F0) estimation in music recordings.
9 |
10 | If you use this toolbox, please cite:
11 |
12 | .. [#] Sebastian Rosenzweig, Simon Schwär, and Meinard Müller. libf0: A Python Library for Fundamental Frequency Estimation. In Late Breaking Demos of the International Society for Music Information Retrieval Conference (ISMIR), Bengaluru, India, 2022.
13 |
14 |
15 | To reference the original algorithms implemented in this toolbox, please cite:
16 |
17 | .. [#] Alain De Cheveigné and Hideki Kawahara: YIN, a fundamental frequency estimator for speech and music, The Journal of the Acoustical Society of America 111.4 (2002): 1917-1930.
18 | .. [#] Matthias Mauch and Simon Dixon: PYIN: A fundamental frequency estimator using probabilistic threshold distributions, IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP) (2014): 659-663.
19 | .. [#] Justin Salamon and Emilia Gómez: Melody Extraction From Polyphonic Music Signals Using Pitch Contour Characteristics, IEEE Transactions on Audio, Speech, and Language Processing, vol. 20, no. 6, pp. 1759–1770, 2012.
20 | .. [#] Arturo Camacho and John G. Harris: A sawtooth waveform inspired pitch estimator for speech and music. The Journal of the Acoustical Society of America, vol. 124, no. 3, pp. 1638–1652, 2008
21 | .. [#] Meinard Müller. Fundamentals of Music Processing – Using Python and Jupyter Notebooks. Springer Verlag, 2nd edition, 2021. ISBN 978-3-030-69807-2. doi: 10.1007/978-3-030-69808-9.
22 |
23 |
24 | .. toctree::
25 | :hidden:
26 |
27 | getting_started
28 |
29 |
30 |
31 | .. toctree::
32 | :caption: API Documentation
33 | :maxdepth: 1
34 | :hidden:
35 |
36 | index_yin
37 | index_pyin
38 | index_salience
39 | index_swipe
40 | index_swipe_slim
41 | index_utils
42 |
--------------------------------------------------------------------------------
/docs/source/index_pyin.rst:
--------------------------------------------------------------------------------
1 | pyin
2 | ==============
3 |
4 | .. automodule:: libf0.pyin
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/source/index_salience.rst:
--------------------------------------------------------------------------------
1 | salience
2 | ==============
3 |
4 | .. automodule:: libf0.salience
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/source/index_swipe.rst:
--------------------------------------------------------------------------------
1 | swipe
2 | ==============
3 |
4 | .. automodule:: libf0.swipe
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/source/index_swipe_slim.rst:
--------------------------------------------------------------------------------
1 | swipe_slim
2 | ==============
3 |
4 | .. automodule:: libf0.swipe_slim
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/source/index_utils.rst:
--------------------------------------------------------------------------------
1 | utils
2 | =====
3 |
4 | .. automodule:: libf0.utils
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/docs/source/index_yin.rst:
--------------------------------------------------------------------------------
1 | yin
2 | ==============
3 |
4 | .. automodule:: libf0.yin
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/libf0/__init__.py:
--------------------------------------------------------------------------------
1 | from .salience import *
2 | from .yin import *
3 | from .pyin import *
4 | from .swipe import *
5 | from .swipe_slim import *
6 | from .utils import *
7 |
--------------------------------------------------------------------------------
/libf0/salience.py:
--------------------------------------------------------------------------------
1 | """
2 | | Description: libf0 salience-based F0 estimation implementation
3 | | Author: Sebastian Rosenzweig, Simon Schwär, Meinard Müller
4 | | License: The MIT license, https://opensource.org/licenses/MIT
5 | | This file is part of libf0.
6 | """
7 | import numpy as np
8 | from librosa import stft
9 | from scipy import ndimage, linalg
10 | from numba import njit
11 |
12 |
13 | def salience(x, Fs=22050, N=2048, H=256, F_min=55.0, F_max=1760.0, R=10.0, num_harm=10, freq_smooth_len=11,
14 | alpha=0.9, gamma=0.0, constraint_region=None, tol=5, score_low=0.01, score_high=1.0):
15 | """
16 | Implementation of a salience-based F0-estimation algorithm using pitch contours, inspired by Melodia.
17 |
18 | .. [#] Justin Salamon and Emilia Gómez,
19 | "Melody Extraction From Polyphonic Music Signals Using Pitch Contour Characteristics."
20 | IEEE Transactions on Audio, Speech, and Language Processing, vol. 20, no. 6, pp. 1759–1770, Aug. 2012.
21 |
22 | Parameters
23 | ----------
24 | x : ndarray
25 | Audio signal
26 | Fs : int
27 | Sampling rate
28 | N : int
29 | Window size
30 | H : int
31 | Hop size
32 | F_min : float or int
33 | Minimal frequency
34 | F_max : float or int
35 | Maximal frequency
36 | R : int
37 | Frequency resolution given in cents
38 | num_harm : int
39 | Number of harmonics (Default value = 10)
40 | freq_smooth_len : int
41 | Filter length for vertical smoothing (Default value = 11)
42 | alpha : float
43 | Weighting parameter for harmonics (Default value = 0.9)
44 | gamma : float
45 | Logarithmic compression factor (Default value = 0.0)
46 | constraint_region : None or ndarray
47 | Constraint regions, row-format: (t_start_sec, t_end_sec, f_start_hz, f_end,hz)
48 | (Default value = None)
49 | tol : int
50 | Tolerance parameter for transition matrix (Default value = 5)
51 | score_low : float
52 | Score (low) for transition matrix (Default value = 0.01)
53 | score_high : float
54 | Score (high) for transition matrix (Default value = 1.0)
55 |
56 | Returns
57 | -------
58 | f0 : ndarray
59 | Estimated F0-trajectory
60 | T_coef: ndarray
61 | Time axis
62 | sal: ndarray
63 | Salience value of estimated F0
64 |
65 | See also
66 | --------
67 | [FMP] Notebook: C8/C8S2_SalienceRepresentation.ipynb
68 | """
69 |
70 | # compute salience representation via instantaneous frequency and harmonic summation
71 | Z, F_coef_hertz = compute_salience_rep(x, Fs, N=N, H=H, F_min=F_min, F_max=F_max, R=R,
72 | num_harm=num_harm, freq_smooth_len=freq_smooth_len,
73 | alpha=alpha, gamma=gamma)
74 |
75 | # compute trajectory via dynamic programming
76 | T_coef = (np.arange(Z.shape[1]) * H) / Fs
77 | index_CR = compute_trajectory_cr(Z, T_coef, F_coef_hertz, constraint_region,
78 | tol=tol, score_low=score_low, score_high=score_high)
79 |
80 | traj = F_coef_hertz[index_CR]
81 | traj[index_CR == -1] = 0
82 |
83 | # compute salience value
84 | Z_max = np.max(Z, axis=0)
85 | Z_norm = np.divide(Z, np.ones((Z.shape[0], 1)) * Z_max)
86 | sal = Z_norm[index_CR, np.arange(Z.shape[1])]
87 | sal[traj == 0] = 0
88 |
89 | return traj, T_coef, sal
90 |
91 |
92 | def compute_salience_rep(x, Fs, N, H, F_min, F_max, R, num_harm, freq_smooth_len, alpha, gamma):
93 | """
94 | Compute salience representation [FMP, Eq. (8.56)]
95 |
96 | Parameters
97 | ----------
98 | x : ndarray
99 | Audio signal
100 | Fs : int
101 | Sampling rate
102 | N : int
103 | Window size
104 | H : int
105 | Hop size
106 | F_min : float or int
107 | Minimal frequency
108 | F_max : float or int
109 | Maximal frequency
110 | R : int
111 | Frequency resolution given in cents
112 | num_harm : int
113 | Number of harmonics
114 | freq_smooth_len : int
115 | Filter length for vertical smoothing
116 | alpha : float
117 | Weighting parameter for harmonics
118 | gamma : float
119 | Logarithmic compression factor
120 |
121 | Returns
122 | -------
123 | Z : ndarray
124 | Salience representation
125 | F_coef_hertz : ndarray
126 | Frequency axis in Hz
127 |
128 | See also
129 | --------
130 | [FMP] Notebook: C8/C8S2_SalienceRepresentation.ipynb
131 | """
132 |
133 | X = stft(x, n_fft=N, hop_length=H, win_length=N, pad_mode='constant')
134 | Y_LF_IF_bin, F_coef_hertz = compute_y_lf_if_bin_eff(X, Fs, N, H, F_min, F_max, R)
135 |
136 | # smoothing
137 | Y_LF_IF_bin = ndimage.convolve1d(Y_LF_IF_bin, np.hanning(freq_smooth_len), axis=0, mode='constant')
138 |
139 | Z = compute_salience_from_logfreq_spec(Y_LF_IF_bin, R, n_harmonics=num_harm, alpha=alpha, beta=1, gamma=gamma)
140 | return Z, F_coef_hertz
141 |
142 |
143 | def compute_y_lf_if_bin_eff(X, Fs, N, H, F_min, F_max, R):
144 | """
145 | Binned Log-frequency Spectrogram with variable frequency resolution based on instantaneous frequency,
146 | more efficient implementation than FMP
147 |
148 | Parameters
149 | ----------
150 | X : ndarray
151 | Complex spectrogram
152 | Fs : int
153 | Sampling rate in Hz
154 | N : int
155 | Window size
156 | H : int
157 | Hop size
158 | F_min : float or int
159 | Minimal frequency
160 | F_max : float or int
161 | Maximal frequency
162 | R : int
163 | Frequency resolution given in cents
164 |
165 | Returns
166 | -------
167 | Y_LF_IF_bin : ndarray
168 | Binned log-frequency spectrogram using instantaneous frequency (shape: [freq, time])
169 | F_coef_hertz : ndarray
170 | Frequency axis in Hz
171 | """
172 |
173 | # calculate number of bins on log frequency axis
174 | B = frequency_to_bin_index(F_max, R, F_min) + 1
175 |
176 | # center frequencies of the final bins
177 | F_coef_hertz = F_min * np.power(2, (np.arange(0, B) * R / 1200))
178 |
179 | # calculate heterodyned phase increment (hpi)
180 | k = np.arange(X.shape[0]).reshape(-1, 1)
181 | omega = 2 * np.pi * k / N # center frequency for each bin in rad
182 | hpi = (np.angle(X[:, 1:]) - np.angle(X[:, 0:-1])) - omega * H
183 |
184 | # reduce hpi to -pi:pi range
185 | # this is much faster than using the modulo function below, but gives the same result
186 | # hpi = np.mod(hpi + np.pi, 2 * np.pi) - np.pi
187 | hpi = hpi - 2 * np.pi * (np.around((hpi / (2 * np.pi)) + 1) - 1)
188 |
189 | # calculate instantaneous frequencies in Hz
190 | inst_f = (omega + hpi / H) * Fs / (2 * np.pi)
191 | # repeat the first time frame to match dimensions of X
192 | inst_f = np.hstack((np.copy(inst_f[:, 0]).reshape(-1, 1), inst_f))
193 |
194 | # mask frequencies that are not relevant
195 | mask = np.logical_and(inst_f >= F_min, inst_f < F_max)
196 | inst_f *= mask
197 | # set 0 to nan, so it does stay at nan in the bin assignment calculation
198 | inst_f[np.where(inst_f == 0)] = np.nan
199 |
200 | # find which inst_f values belong to which bin
201 | bin_assignment = frequency_to_bin_index(inst_f, R, F_min)
202 | # we map the discarded values to an extra bin that we remove before returning the binned spectrogram
203 | bin_assignment[np.where(np.isnan(inst_f))] = B
204 |
205 | # perform binning on power spectrogram for each time frame separately
206 | Y = np.abs(X) ** 2
207 | Y_LF_IF_bin = np.zeros((B+1, Y.shape[1]))
208 | for t in range(Y.shape[1]):
209 | np.add.at(Y_LF_IF_bin[:, t], bin_assignment[:, t], Y[:, t])
210 |
211 | return Y_LF_IF_bin[:B, :], F_coef_hertz
212 |
213 |
214 | def compute_salience_from_logfreq_spec(lf_spec, R, n_harmonics, alpha, beta, gamma, harmonic_win_len=11):
215 | """
216 | Compute salience representation using harmonic summation following [1]
217 |
218 | [1] J. Salamon and E. Gomez,
219 | "Melody Extraction From Polyphonic Music Signals Using Pitch Contour Characteristics."
220 | IEEE Transactions on Audio, Speech, and Language Processing, vol. 20, no. 6, pp. 1759–1770, Aug. 2012.
221 |
222 | Parameters
223 | ----------
224 | lf_spec : ndarray
225 | (F, T) log-spectrogram
226 | R : int
227 | Frequency resolution given in cents
228 | n_harmonics : int
229 | Number of harmonics
230 | alpha : float
231 | Weighting parameter for harmonics
232 | beta : float
233 | Compression parameter for spectrogram magnitudes
234 | gamma : float
235 | Magnitude threshold
236 | harmonic_win_len : int
237 | Length of a frequency weighting window in bins
238 |
239 | Returns
240 | -------
241 | Z : ndarray
242 | (F, T) salience representation of the input spectrogram
243 | """
244 |
245 | # magnitude thresholding and compression
246 | eps = np.finfo(np.float32).eps
247 | threshold_mask = (20 * np.log10(lf_spec/np.max(lf_spec) + eps)) < gamma
248 | lf_spec = lf_spec**beta * threshold_mask
249 |
250 | # compute window
251 | max_diff_bins = harmonic_win_len // 2
252 | window = np.cos(np.linspace(-1, 1, 2*max_diff_bins+1)*np.pi/2)**2 # cosine^2 window
253 |
254 | # compute indices of harmonics
255 | harmonics = np.round(np.log2(np.arange(1, n_harmonics + 1)) * 1200 / R).astype(int)
256 | weighting_vec = np.zeros((lf_spec.shape[0] + max_diff_bins))
257 |
258 | # compute weights
259 | for idx, h in enumerate(harmonics):
260 | if h+harmonic_win_len > len(weighting_vec):
261 | break # we reached the maximum length available
262 | weighting_vec[h:h+harmonic_win_len] += window * alpha**idx
263 |
264 | # correlate lf_spec with the weighting vector on the frequency axis
265 | Z = ndimage.correlate1d(lf_spec, weighting_vec[:],
266 | axis=0, mode='constant', cval=0, origin=-len(weighting_vec)//2 + max_diff_bins)
267 |
268 | # magnitude thresholding and compression
269 | threshold_mask = (20 * np.log10(Z / np.max(Z) + eps)) < gamma
270 | Z = Z ** beta * threshold_mask
271 |
272 | return Z
273 |
274 |
275 | def define_transition_matrix(B, tol=0, score_low=0.01, score_high=1.0):
276 | """
277 | Generate transition matrix for dynamic programming
278 |
279 | Parameters
280 | ----------
281 | B : int
282 | Number of bins
283 | tol : int
284 | Tolerance parameter for transition matrix (Default value = 0)
285 | score_low : float
286 | Score (low) for transition matrix (Default value = 0.01)
287 | score_high : float
288 | Score (high) for transition matrix (Default value = 1.0)
289 |
290 | Returns
291 | -------
292 | T : ndarray
293 | (B, B) Transition matrix
294 |
295 | See also
296 | --------
297 | [FMP] Notebook: C8/C8S2_FundFreqTracking.ipynb
298 | """
299 |
300 | col = np.ones((B,)) * score_low
301 | col[0:tol+1] = np.ones((tol+1, )) * score_high
302 | T = linalg.toeplitz(col)
303 | return T
304 |
305 |
306 | @njit
307 | def compute_trajectory_dp(Z, T):
308 | """
309 | Trajectory tracking using dynamic programming
310 |
311 | Parameters
312 | ----------
313 | Z : ndarray
314 | Salience representation
315 | T : ndarray
316 | Transisition matrix
317 |
318 | Returns
319 | -------
320 | eta_DP : ndarray
321 | Trajectory indices
322 |
323 | See also
324 | --------
325 | [FMP] Notebook: C8/C8S2_FundFreqTracking.ipynb
326 | """
327 |
328 | B, N = Z.shape
329 | eps_machine = np.finfo(np.float32).eps
330 | Z_log = np.log(Z + eps_machine)
331 | T_log = np.log(T + eps_machine)
332 |
333 | E = np.zeros((B, N))
334 | D = np.zeros((B, N))
335 | D[:, 0] = Z_log[:, 0]
336 |
337 | for n in np.arange(1, N):
338 | for b in np.arange(0, B):
339 | D[b, n] = np.max(T_log[b, :] + D[:, n-1]) + Z_log[b, n]
340 | E[b, n-1] = np.argmax(T_log[b, :] + D[:, n-1])
341 |
342 | # backtracking
343 | eta_DP = np.zeros(N)
344 | eta_DP[N-1] = int(np.argmax(D[:, N-1]))
345 |
346 | for n in np.arange(N-2, -1, -1):
347 | eta_DP[n] = E[int(eta_DP[n+1]), n]
348 |
349 | return eta_DP.astype(np.int64)
350 |
351 |
352 | def compute_trajectory_cr(Z, T_coef, F_coef_hertz, constraint_region=None,
353 | tol=5, score_low=0.01, score_high=1.0):
354 | """
355 | Trajectory tracking with constraint regions
356 | Notebook: C8/C8S2_FundFreqTracking.ipynb
357 |
358 | Parameters
359 | ----------
360 | Z : ndarray
361 | Salience representation
362 | T_coef : ndarray
363 | Time axis
364 | F_coef_hertz : ndarray
365 | Frequency axis in Hz
366 | constraint_region : ndarray or None
367 | Constraint regions, row-format: (t_start_sec, t_end_sec, f_start_hz, f_end_hz)
368 | (Default value = None)
369 | tol : int
370 | Tolerance parameter for transition matrix (Default value = 5)
371 | score_low : float
372 | Score (low) for transition matrix (Default value = 0.01)
373 | score_high : float
374 | Score (high) for transition matrix (Default value = 1.0)
375 |
376 | Returns
377 | -------
378 | eta : ndarray
379 | Trajectory indices, unvoiced frames are indicated with -1
380 |
381 | See also
382 | --------
383 | [FMP] Notebook: C8/C8S2_FundFreqTracking.ipynb
384 | """
385 |
386 | # do tracking within every constraint region
387 | if constraint_region is not None:
388 | # initialize contour, unvoiced frames are indicated with -1
389 | eta = np.full(len(T_coef), -1)
390 |
391 | for row_idx in range(constraint_region.shape[0]):
392 | t_start = constraint_region[row_idx, 0] # sec
393 | t_end = constraint_region[row_idx, 1] # sec
394 | f_start = constraint_region[row_idx, 2] # Hz
395 | f_end = constraint_region[row_idx, 3] # Hz
396 |
397 | # convert start/end values to indices
398 | t_start_idx = np.argmin(np.abs(T_coef - t_start))
399 | t_end_idx = np.argmin(np.abs(T_coef - t_end))
400 | f_start_idx = np.argmin(np.abs(F_coef_hertz - f_start))
401 | f_end_idx = np.argmin(np.abs(F_coef_hertz - f_end))
402 |
403 | # track in salience part
404 | cur_Z = Z[f_start_idx:f_end_idx+1, t_start_idx:t_end_idx+1]
405 | T = define_transition_matrix(cur_Z.shape[0], tol=tol,
406 | score_low=score_low, score_high=score_high)
407 | cur_eta = compute_trajectory_dp(cur_Z, T)
408 |
409 | # fill contour
410 | eta[t_start_idx:t_end_idx+1] = f_start_idx + cur_eta
411 | else:
412 | T = define_transition_matrix(Z.shape[0], tol=tol, score_low=score_low, score_high=score_high)
413 | eta = compute_trajectory_dp(Z, T)
414 |
415 | return eta
416 |
417 |
418 | def frequency_to_bin_index(F, R, F_ref):
419 | """
420 | Binning function with variable frequency resolution
421 | Note: Indexing starts with 0 (opposed to [FMP, Eq. (8.49)])
422 |
423 | Parameters
424 | ----------
425 | F : float or ndarray
426 | Frequency in Hz
427 | R : float
428 | Frequency resolution in cents (Default value = 10.0)
429 | F_ref : float
430 | Reference frequency in Hz (Default value = 55.0)
431 |
432 | Returns
433 | -------
434 | bin_index (int): Index for bin (starting with index 0)
435 |
436 | See also
437 | --------
438 | [FMP] Notebook: C8/C8S2_SalienceRepresentation.ipynb
439 | """
440 | bin_index = np.floor((1200 / R) * np.log2(F / F_ref) + 0.5).astype(np.int64)
441 | return bin_index
442 |
--------------------------------------------------------------------------------
/libf0/swipe.py:
--------------------------------------------------------------------------------
1 | """
2 | | Description: libf0 SWIPE implementation
3 | | Contributors: Sebastian Rosenzweig, Vojtěch Pešek, Simon Schwär, Meinard Müller
4 | | License: The MIT license, https://opensource.org/licenses/MIT
5 | | This file is part of libf0.
6 | """
7 | from scipy import interpolate
8 | import numpy as np
9 | import librosa
10 |
11 |
12 | def swipe(x, Fs=22050, H=256, F_min=55.0, F_max=1760.0, dlog2p=1 / 96, derbs=0.1, strength_threshold=0):
13 | """
14 | Implementation of a sawtooth waveform inspired pitch estimator (SWIPE).
15 | This version of the algorithm follows the original implementation, see `swipe_slim` for a more efficient
16 | alternative.
17 |
18 | .. [#] Arturo Camacho and John G. Harris,
19 | "A sawtooth waveform inspired pitch estimator for speech and music."
20 | The Journal of the Acoustical Society of America, vol. 124, no. 3, pp. 1638–1652, Sep. 2008
21 |
22 | Parameters
23 | ----------
24 | x : ndarray
25 | Audio signal
26 | Fs : int
27 | Sampling rate
28 | H : int
29 | Hop size
30 | F_min : float or int
31 | Minimal frequency
32 | F_max : float or int
33 | Maximal frequency
34 | dlog2p : float
35 | resolution of the pitch candidate bins in octaves (default value = 1/96 -> 96 bins per octave)
36 | derbs : float
37 | resolution of the ERB bands (default value = 0.1)
38 | strength_threshold : float
39 | confidence threshold [0, 1] for the pitch detection (default value = 0)
40 |
41 | Returns
42 | -------
43 | f0 : ndarray
44 | Estimated F0-trajectory
45 | t : ndarray
46 | Time axis
47 | strength : ndarray
48 | Confidence/Pitch Strength
49 | """
50 |
51 | t = np.arange(0, len(x), H) / Fs # Times
52 |
53 | # Compute pitch candidates
54 | pc = 2 ** np.arange(np.log2(F_min), np.log2(F_max), dlog2p)
55 |
56 | # Pitch strength matrix
57 | S = np.zeros((len(pc), len(t)))
58 |
59 | # Determine P2-WSs [max, min]
60 | log_ws_max = np.ceil(np.log2((8 / F_min) * Fs))
61 | log_ws_min = np.floor(np.log2((8 / F_max) * Fs))
62 |
63 | # P2-WSs - window sizes in samples
64 | ws = 2 ** np.arange(log_ws_max, log_ws_min - 1, -1, dtype=np.int32)
65 | # print(f'window sizes in samples: {ws}')
66 |
67 | # Determine window sizes used by each pitch candidate
68 | log2pc = np.arange(np.log2(F_min), np.log2(F_max), dlog2p)
69 | d = log2pc - np.log2(np.divide(8 * Fs, ws[0]))
70 |
71 | # Create ERBs spaced frequencies (in Hertz)
72 | fERBs = erbs2hz(np.arange(hz2erbs(pc[0] / 4), hz2erbs(Fs / 2), derbs))
73 |
74 | for i in range(0, len(ws)):
75 | N = ws[i]
76 | H = int(N / 2)
77 |
78 | x_zero_padded = np.concatenate([x, np.zeros(N)])
79 |
80 | X = librosa.stft(x_zero_padded, n_fft=N, hop_length=H, pad_mode='constant', center=True)
81 | ti = librosa.frames_to_time(np.arange(0, X.shape[1]), sr=Fs, hop_length=H, n_fft=N)
82 | f = librosa.fft_frequencies(sr=Fs, n_fft=N)
83 |
84 | ti = np.insert(ti, 0, 0)
85 | ti = np.delete(ti, -1)
86 |
87 | spectrum = np.abs(X)
88 | magnitude = resample_ferbs(spectrum, f, fERBs)
89 | loudness = np.sqrt(magnitude)
90 |
91 | # Select candidates that use this window size
92 | # First window
93 | if i == 0:
94 | j = np.argwhere(d < 1).flatten()
95 | k = np.argwhere(d[j] > 0).flatten()
96 | # Last Window
97 | elif i == len(ws) - 1:
98 | j = np.argwhere(d - i > -1).flatten()
99 | k = np.argwhere(d[j] - i < 0).flatten()
100 | else:
101 | j = np.argwhere(np.abs(d - i) < 1).flatten()
102 | k = np.arange(0, len(j))
103 |
104 | pc_to_compute = pc[j]
105 |
106 | pitch_strength = pitch_strength_all_candidates(fERBs, loudness, pc_to_compute)
107 |
108 | resampled_pitch_strength = resample_time(pitch_strength, t, ti)
109 |
110 | lambda_ = d[j[k]] - i
111 | mu = np.ones(len(j))
112 | mu[k] = 1 - np.abs(lambda_)
113 |
114 | S[j, :] = S[j, :] + np.multiply(
115 | np.ones(resampled_pitch_strength.shape) * mu.reshape((mu.shape[0], 1)),
116 | resampled_pitch_strength
117 | )
118 |
119 | # Fine-tune the pitch using parabolic interpolation
120 | pitches, strength = parabolic_int(S, strength_threshold, pc)
121 |
122 | pitches[np.where(np.isnan(pitches))] = 0 # avoid NaN output
123 |
124 | return pitches, t, strength
125 |
126 |
127 | def nyquist(Fs):
128 | """Nyquist Frequency"""
129 | return Fs / 2
130 |
131 |
132 | def F_coef(k, N, Fs):
133 | """Physical frequency of STFT coefficients"""
134 | return (k * Fs) / N
135 |
136 |
137 | def T_coef(m, H, Fs):
138 | """Physical time of STFT coefficients"""
139 | return m * H / Fs
140 |
141 |
142 | def stft_with_f_t(y, N, H, Fs):
143 | """STFT wrapper"""
144 | x = librosa.stft(y, int(N), int(H), pad_mode='constant', center=True)
145 | f = F_coef(np.arange(0, x.shape[0]), N, Fs)
146 | t = T_coef(np.arange(0, x.shape[1]), H, Fs)
147 |
148 | return x, f, t
149 |
150 |
151 | def hz2erbs(hz):
152 | """Convert Hz to ERB scale"""
153 | return 21.4 * np.log10(1 + hz / 229)
154 |
155 |
156 | def erbs2hz(erbs):
157 | """Convert ERB to Hz"""
158 | return (10 ** np.divide(erbs, 21.4) - 1) * 229
159 |
160 |
161 | def pitch_strength_all_candidates(ferbs, loudness, pitch_candidates):
162 | """Compute pitch strength for all pitch candidates"""
163 | # Normalize loudness
164 | normalization_loudness = np.full_like(loudness, np.sqrt(np.sum(loudness * loudness, axis=0)))
165 | with np.errstate(divide='ignore', invalid='ignore'):
166 | loudness = loudness / normalization_loudness
167 |
168 | # Create pitch salience matrix
169 | S = np.zeros((len(pitch_candidates), loudness.shape[1]))
170 |
171 | for j in range(0, len(pitch_candidates)):
172 | S[j, :] = pitch_strength_one(ferbs, loudness, pitch_candidates[j])
173 | return S
174 |
175 |
176 | def pitch_strength_one(erbs_frequencies, normalized_loudness, pitch_candidate):
177 | """Compute pitch strength for one pitch candidate"""
178 | number_of_harmonics = np.floor(erbs_frequencies[-1] / pitch_candidate - 0.75).astype(np.int32)
179 | k = np.zeros(erbs_frequencies.shape)
180 |
181 | # f_prime / f
182 | q = erbs_frequencies / pitch_candidate
183 |
184 | for i in np.concatenate(([1], primes(number_of_harmonics))):
185 | a = np.abs(q - i)
186 | p = a < 0.25
187 | k[p] = np.cos(np.dot(2 * np.pi, q[p]))
188 | v = np.logical_and(0.25 < a, a < 0.75)
189 | k[v] = k[v] + np.cos(np.dot(2 * np.pi, q[v])) / 2
190 |
191 | # Apply envelope
192 | k = np.multiply(k, np.sqrt(1.0 / erbs_frequencies))
193 |
194 | # K+-normalize kernel
195 | k = k / np.linalg.norm(k[k > 0])
196 |
197 | # Compute pitch strength
198 | S = np.dot(k, normalized_loudness)
199 | return S
200 |
201 |
202 | def resample_ferbs(spectrum, f, ferbs):
203 | """Resample to ERB scale"""
204 | magnitude = np.zeros((len(ferbs), spectrum.shape[1]))
205 |
206 | for t in range(spectrum.shape[1]):
207 | spl = interpolate.splrep(f, spectrum[:, t])
208 | interpolate.splev(ferbs, spl)
209 |
210 | magnitude[:, t] = interpolate.splev(ferbs, spl)
211 |
212 | return np.maximum(magnitude, 0)
213 |
214 |
215 | def resample_time(pitch_strength, resampled_time, ti):
216 | """Resample time axis"""
217 | if pitch_strength.shape[1] > 0:
218 | pitch_strength = interpolate_one_candidate(pitch_strength, ti, resampled_time)
219 | else:
220 | pitch_strength = np.kron(np.ones((len(pitch_strength), len(resampled_time))), np.NaN)
221 | return pitch_strength
222 |
223 |
224 | def interpolate_one_candidate(pitch_strength, ti, resampled_time):
225 | """Interpolate time axis"""
226 | pitch_strength_interpolated = np.zeros((pitch_strength.shape[0], len(resampled_time)))
227 |
228 | for s in range(pitch_strength.shape[0]):
229 | t_i = interpolate.interp1d(ti, pitch_strength[s, :], 'linear', bounds_error=True)
230 | pitch_strength_interpolated[s, :] = t_i(resampled_time)
231 |
232 | return pitch_strength_interpolated
233 |
234 |
235 | def parabolic_int(pitch_strength, strength_threshold, pc):
236 | """Parabolic interpolation between pitch candidates using pitch strength"""
237 | p = np.full((pitch_strength.shape[1],), np.NaN)
238 | s = np.full((pitch_strength.shape[1],), np.NaN)
239 |
240 | for j in range(pitch_strength.shape[1]):
241 | i = np.argmax(pitch_strength[:, j])
242 | s[j] = pitch_strength[i, j]
243 |
244 | if s[j] < strength_threshold:
245 | continue
246 |
247 | if i == 0:
248 | p[j] = pc[0]
249 | elif i == len(pc) - 1:
250 | p[j] = pc[0]
251 | else:
252 | I = np.arange(i - 1, i + 2)
253 | tc = 1 / pc[I]
254 | ntc = np.dot((tc / tc[1] - 1), 2 * np.pi)
255 | if np.any(np.isnan(pitch_strength[I, j])):
256 | s[j] = np.nan
257 | p[j] = np.nan
258 | else:
259 | c = np.polyfit(ntc, pitch_strength[I, j], 2)
260 | ftc = 1 / 2 ** np.arange(np.log2(pc[I[0]]), np.log2(pc[I[2]]), 1 / 12 / 64)
261 | nftc = np.dot((ftc / tc[1] - 1), 2 * np.pi)
262 | poly = np.polyval(c, nftc)
263 | k = np.argmax(poly)
264 | s[j] = poly[k]
265 | p[j] = 2 ** (np.log2(pc[I[0]]) + k / 12 / 64)
266 | return p, s
267 |
268 |
269 | def primes(n):
270 | """Returns a set of n prime numbers"""
271 | small_primes = np.array([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89,
272 | 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
273 | 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
274 | 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
275 | 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
276 | 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619,
277 | 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743,
278 | 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
279 | 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997])
280 |
281 | b = small_primes <= n
282 | return small_primes[b]
283 |
--------------------------------------------------------------------------------
/libf0/swipe_slim.py:
--------------------------------------------------------------------------------
1 | """
2 | | Description: libf0 SWIPE slim implementation
3 | | Contributors: Sebastian Rosenzweig, Simon Schwär, Meinard Müller
4 | | License: The MIT license, https://opensource.org/licenses/MIT
5 | | This file is part of libf0.
6 | """
7 | import numpy as np
8 | import librosa
9 | from .yin import parabolic_interpolation
10 | from scipy.interpolate import interp1d
11 |
12 |
13 | def swipe_slim(x, Fs=22050, H=256, F_min=55.0, F_max=1760.0, R=10, strength_threshold=0):
14 | """
15 | Slim and didactical implementation of a sawtooth waveform inspired pitch estimator (SWIPE).
16 | This version uses a log-frequency spectrogram instead of ERB filters. Furthermore, it is implemented more
17 | efficiently. See `swipe()` for the original implementation.
18 |
19 | .. [#] A. Camacho and J. G. Harris,
20 | "A sawtooth waveform inspired pitch estimator for speech and music."
21 | The Journal of the Acoustical Society of America, vol. 124, no. 3, pp. 1638–1652, Sep. 2008
22 |
23 | Parameters
24 | ----------
25 | x : ndarray
26 | Audio signal
27 | Fs : int
28 | Sampling rate
29 | H : int
30 | Hop size
31 | F_min : float or int
32 | Minimal frequency
33 | F_max : float or int
34 | Maximal frequency
35 | R : float
36 | resolution of the pitch candidate bins in cents (default = 10)
37 | strength_threshold : float
38 | confidence threshold [0, 1] for the pitch detection (default value = 0)
39 |
40 | Returns
41 | -------
42 | f0 : ndarray
43 | Estimated F0-trajectory
44 | t : ndarray
45 | Time axis
46 | conf : ndarray
47 | Confidence / Pitch Strength
48 | """
49 |
50 | # compute time and frequency axis
51 | t = np.arange(0, len(x), H) / Fs # time axis
52 | F_coef_log = np.arange(0, np.log2(Fs/2/F_min), R/1200)
53 | F_coef_log_hz = F_min * 2 ** F_coef_log # pitch candidates
54 |
55 | # pre-compute kernels, one kernel for each pitch candidate in range [F_min : F_max]
56 | F_min_idx = np.argmin(np.abs(F_coef_log_hz - F_min))
57 | F_max_idx = np.argmin(np.abs(F_coef_log_hz - F_max))
58 | B = F_max_idx - F_min_idx # Number of pitch candidates
59 | kernels = np.zeros((B, len(F_coef_log_hz)))
60 | for i, f in enumerate(F_coef_log_hz[F_min_idx:F_max_idx]):
61 | kernels[i, :] = compute_kernel(f, F_coef_log_hz)
62 |
63 | # determine optimal window length for each candidate
64 | L_opt = np.log2(Fs * 8 / np.array([F_min, F_max])) # exponents for optimal window sizes 2^L, see paper Section II.G
65 | L_rnd = np.arange(np.round(L_opt[1]), np.round(L_opt[0])+1).astype(np.int32) # range of rounded exponents
66 | N_pow2 = 2 ** L_rnd # Compute rounded power-2 windows sizes
67 | # Quantization error between optimal window size (see paper Section II.G) and rounded power-2 windows size
68 | # Using only the largest N here, since errors for other N can be derived from err by subtracting exponent (cyclic)
69 | err = np.abs(np.log2(8 * Fs / F_coef_log_hz[F_min_idx:F_max_idx]) - np.log2(np.max(N_pow2)))
70 |
71 | S = np.zeros((B, len(t))) # "pitch-strength" matrix
72 |
73 | # loop through all window sizes
74 | for octave, N in enumerate(N_pow2):
75 | # Compute STFT
76 | x_pad = np.pad(x, (0, N)) # to avoid problems during time axis interpolation
77 | H = N // 2
78 | X = librosa.stft(x_pad, n_fft=N, hop_length=H, win_length=N, window='hann', pad_mode='constant', center=True)
79 | Y = np.abs(X)
80 | T_coef_lin_s = np.arange(0, X.shape[1]) * H / Fs
81 | F_coef_lin_hz = np.arange(N // 2 + 1) * Fs / N
82 |
83 | # Resample to log-frequency axis
84 | compute_Y_log = interp1d(F_coef_lin_hz, Y, kind='cubic', axis=0)
85 | Y_log = compute_Y_log(F_coef_log_hz)
86 |
87 | # Normalize magnitudes
88 | Y_log /= np.sqrt(np.sum(Y_log ** 2, axis=0)) + np.finfo(float).eps
89 |
90 | # Correlate kernels with log-spectrum for pitch candidates where N is optimal
91 | S_N = np.matmul(kernels, Y_log)
92 |
93 | # Resample time axis
94 | compute_S_N_res = interp1d(T_coef_lin_s, S_N, kind='linear', axis=1)
95 | S_N_res = compute_S_N_res(t)
96 |
97 | # Weight pitch strength according to quantization error
98 | candidates = (err > octave - 1) & (err < octave + 1) # consider pitches +/- 1 octave from current window
99 | mu = 1 - np.abs(err[candidates] - octave)
100 |
101 | S[candidates, :] += np.multiply(mu.reshape(-1, 1), S_N_res[candidates, :])
102 |
103 | # Obtain pitch estimates and corresponding confidence
104 | max_indices = np.argmax(S, axis=0)
105 | conf = np.max(S, axis=0)
106 |
107 | # Parabolic Interpolation of pitch estimates for refinement
108 | time_idx = np.arange(S.shape[1])
109 | indeces_shift, _ = parabolic_interpolation(S[max_indices-1, time_idx],
110 | S[max_indices, time_idx],
111 | S[max_indices+1, time_idx])
112 | compute_f0_log = interp1d(np.arange(len(F_coef_log)), F_coef_log, kind='linear')
113 | f0_hz = F_min * 2 ** compute_f0_log(max_indices+indeces_shift)
114 |
115 | # Thresholding
116 | f0_hz[conf < strength_threshold] = 0 # discard estimates where confidence is low
117 |
118 | return f0_hz, t, conf
119 |
120 |
121 | def compute_kernel(f, F_coef_log_hz):
122 | """
123 | Compute a SWIPE' kernel.
124 |
125 | Parameters
126 | ----------
127 | f : float
128 | Frequency in Hz
129 | F_coef_log_hz :
130 | Logarithmic frequency axis in Hz
131 |
132 | Returns
133 | -------
134 | k : ndarray
135 | Kernel
136 | """
137 | k = np.zeros(len(F_coef_log_hz))
138 | n_harmonics = np.floor(F_coef_log_hz[-1] / f).astype(np.int32)
139 | prime_numbers = prime_and_one(100)[:n_harmonics] # only consider prime harmonics for kernel peaks
140 |
141 | ratio = F_coef_log_hz / f
142 |
143 | # loop through all prime harmonics
144 | for p in prime_numbers:
145 | a = np.abs(ratio - p) # normalized distance between harmonic and current pitch candidate
146 | main_peak_bins = a < 0.25
147 | k[main_peak_bins] = np.cos(np.dot(np.array(2 * np.pi).reshape(-1, 1),
148 | ratio[main_peak_bins].reshape(1, -1))).flatten()
149 | valley_bins = np.logical_and(0.25 < a, a < 0.75)
150 | k[valley_bins] += np.cos(np.dot(np.array(2 * np.pi).reshape(-1, 1),
151 | ratio[valley_bins].reshape(1, -1))).flatten() / 2
152 |
153 | # Apply decay
154 | k = np.multiply(k, np.sqrt(1.0 / F_coef_log_hz))
155 |
156 | # K+-normalize kernel
157 | k = k / np.linalg.norm(k[k > 0])
158 |
159 | return k
160 |
161 |
162 | def prime_and_one(upto=1000000):
163 | """
164 | Returns a set of prime numbers, adapted from http://rebrained.com/?p=458
165 |
166 | Parameters
167 | ----------
168 | upto : int
169 | Find prime numbers up to this number
170 |
171 | Returns
172 | -------
173 | A set of prime numbers including 1 & 2
174 | """
175 | primes = np.arange(3, upto+1, 2)
176 | isprime = np.ones((upto-1)//2, dtype=np.bool8)
177 | for factor in primes[:int(np.sqrt(upto))//2]:
178 | if isprime[(factor-2)//2]:
179 | isprime[(factor*3-2)//2::factor] = 0
180 | return np.concatenate((np.array([1, 2]), primes[isprime]))
181 |
--------------------------------------------------------------------------------
/libf0/utils.py:
--------------------------------------------------------------------------------
1 | """
2 | | Description: libf0 utility functions
3 | | Contributors: Sebastian Rosenzweig, Simon Schwär, Meinard Müller
4 | | License: The MIT license, https://opensource.org/licenses/MIT
5 | | This file is part of libf0.
6 | """
7 | import numpy as np
8 |
9 |
10 | def sonify_trajectory_with_sinusoid(f0, t, audio_len, confidence=None, Fs=22050, smooth_len=11):
11 | """
12 | Sonification of trajectory with sinusoidal. Adapted from FMP notebook: C8/C8S2_FundFreqTracking.ipynb
13 |
14 | Parameters
15 | ----------
16 | f0 : ndarray
17 | F0-trajectory
18 | t : ndarray
19 | Time axis
20 | audio_len : int
21 | Desired audio length in samples
22 | confidence : None or ndarray
23 | Confidence values for amplitude control
24 | Fs : int
25 | Sampling rate
26 | smooth_len : int
27 | Smoothing filter length to avoid clicks in the sonification
28 |
29 | Returns
30 | -------
31 | x_soni : ndarray
32 | Sonified F0-trajectory
33 | """
34 | if confidence is None:
35 | confidence = np.ones_like(f0)
36 |
37 | # initialize
38 | x_soni = np.zeros(audio_len)
39 | amplitude_mod = np.zeros(audio_len)
40 |
41 | # Computation of hop size
42 | sine_len = int(t[1] * Fs)
43 |
44 | t = np.arange(0, sine_len) / Fs
45 | phase = 0
46 |
47 | # loop over all F0 values, ensure continuous phase
48 | for idx in np.arange(0, len(f0)):
49 | cur_f = f0[idx]
50 | cur_amp = confidence[idx]
51 |
52 | if cur_f == 0:
53 | phase = 0
54 | continue
55 |
56 | cur_soni = np.sin(2*np.pi*(cur_f*t+phase))
57 | diff = np.maximum(0, (idx+1)*sine_len - len(x_soni))
58 | if diff > 0:
59 | x_soni[idx * sine_len:(idx + 1) * sine_len - diff] = cur_soni[:-diff]
60 | amplitude_mod[idx * sine_len:(idx + 1) * sine_len - diff] = cur_amp
61 | else:
62 | x_soni[idx*sine_len:(idx+1)*sine_len-diff] = cur_soni
63 | amplitude_mod[idx*sine_len:(idx+1)*sine_len-diff] = cur_amp
64 |
65 | phase += cur_f * sine_len / Fs
66 | phase -= 2 * np.round(phase/2)
67 |
68 | # filter amplitudes to avoid transients
69 | amplitude_mod = np.convolve(amplitude_mod, np.hanning(smooth_len)/np.sum(np.hanning(smooth_len)), 'same')
70 | x_soni = x_soni * amplitude_mod
71 | return x_soni
72 |
73 |
74 | def hz_to_cents(F, F_ref=55.0):
75 | """
76 | Converts frequency in Hz to cents.
77 |
78 | Parameters
79 | ----------
80 | F : float or ndarray
81 | Frequency value in Hz
82 | F_ref : float
83 | Reference frequency in Hz (Default value = 55.0)
84 | Returns
85 | -------
86 | F_cents : float or ndarray
87 | Frequency in cents
88 | """
89 |
90 | # Avoid division by 0
91 | F_temp = np.array(F).astype(float)
92 | F_temp[F_temp == 0] = np.nan
93 |
94 | F_cents = 1200 * np.log2(F_temp / F_ref)
95 |
96 | return F_cents
97 |
98 |
99 | def cents_to_hz(F_cents, F_ref=55.0):
100 | """
101 | Converts frequency in cents to Hz.
102 |
103 | Parameters
104 | ----------
105 | F_cents : float or ndarray
106 | Frequency in cents
107 | F_ref : float
108 | Reference frequency in Hz (Default value = 55.0)
109 | Returns
110 | -------
111 | F : float or ndarray
112 | Frequency in Hz
113 | """
114 | F = F_ref * 2 ** (F_cents / 1200)
115 |
116 | # Avoid NaN output
117 | F = np.nan_to_num(F, copy=False, nan=0)
118 |
119 | return F
120 |
--------------------------------------------------------------------------------
/libf0/yin.py:
--------------------------------------------------------------------------------
1 | """
2 | | Description: libf0 YIN implementation
3 | | Contributors: Sebastian Rosenzweig, Simon Schwär, Edgar Suárez, Meinard Müller
4 | | License: The MIT license, https://opensource.org/licenses/MIT
5 | | This file is part of libf0.
6 | """
7 | import numpy as np
8 | from numba import njit
9 |
10 |
11 | def yin(x, Fs=22050, N=2048, H=256, F_min=55.0, F_max=1760.0, threshold=0.15, verbose=False):
12 | """
13 | Implementation of the YIN algorithm.
14 |
15 | .. [#] Alain De Cheveigné and Hideki Kawahara.
16 | "YIN, a fundamental frequency estimator for speech and music."
17 | The Journal of the Acoustical Society of America 111.4 (2002): 1917-1930.
18 |
19 | Parameters
20 | ----------
21 | x : ndarray [shape=(L, )], real - valued
22 | Audio signal
23 | Fs : int
24 | Sampling frequency
25 | N : int
26 | Window size
27 | H : int
28 | Hop size
29 | F_min : float
30 | Minimal frequency
31 | F_max : float
32 | Maximal frequency
33 | threshold : float
34 | Threshold for cumulative mean normalized difference function
35 | verbose : bool
36 | Switch to activate/deactivate status bar
37 |
38 | Returns
39 | -------
40 | f0 : ndarray
41 | Estimated F0-trajectory
42 | t : ndarray
43 | Time axis
44 | ap: ndarray
45 | Aperiodicity (indicator for voicing: the lower, the more reliable the estimate)
46 | """
47 |
48 | if F_min > F_max:
49 | raise Exception("F_min must be smaller than F_max!")
50 |
51 | if F_min < Fs/N:
52 | raise Exception(f"The condition (F_min >= Fs/N) was not met. With Fs = {Fs}, N = {N} and F_min = {F_min} you have the following options: \n1) Set F_min >= {np.ceil(Fs/N)} Hz. \n2) Set N >= {np.ceil(Fs/F_min).astype(int)}. \n3) Set Fs <= {np.floor(F_min * N)} Hz.")
53 |
54 | x_pad = np.concatenate((np.zeros(N//2), x, np.zeros(N//2))) # Add zeros for centered estimates
55 | M = int(np.floor((len(x_pad) - N) / H)) + 1 # Compute number of estimates that will be generated
56 | f0 = np.zeros(M) # Estimated fundamental frequencies (0 for unspecified frames)
57 | t = np.arange(M)*H/Fs # Time axis
58 | ap = np.zeros(M) # Aperiodicity
59 |
60 | lag_min = max(int(np.ceil(Fs / F_max)), 1) # lag of maximal frequency in samples
61 | lag_max = int(np.ceil(Fs / F_min)) # lag of minimal frequency in samples
62 |
63 | for m in range(M):
64 | if verbose:
65 | print(f"YIN Progress: {np.ceil(100*m/M).astype(int)}%", end='\r')
66 | # Take a frame from input signal
67 | frame = x_pad[m*H:m*H + N]
68 |
69 | # Cumulative Mean Normalized Difference Function
70 | cmndf = cumulative_mean_normalized_difference_function(frame, lag_max)
71 |
72 | # Absolute Thresholding
73 | lag_est = absolute_thresholding(cmndf, threshold, lag_min, lag_max, parabolic_interp=True)
74 |
75 | # Refine estimate by constraining search to vicinity of best local estimate (default: +/- 25 cents)
76 | tol_cents = 25
77 | lag_min_local = int(np.round(Fs / ((Fs / lag_est) * 2 ** (tol_cents/1200))))
78 | if lag_min_local < lag_min:
79 | lag_min_local = lag_min
80 | lag_max_local = int(np.round(Fs / ((Fs / lag_est) * 2 ** (-tol_cents/1200))))
81 | if lag_max_local > lag_max:
82 | lag_max_local = lag_max
83 | lag_new = absolute_thresholding(cmndf, threshold=np.inf, lag_min=lag_min_local, lag_max=lag_max_local,
84 | parabolic_interp=True)
85 |
86 | # Compute Fundamental Frequency Estimate
87 | f0[m] = Fs / lag_new
88 |
89 | # Compute Aperiodicity
90 | ap[m] = aperiodicity(frame, lag_new)
91 |
92 | return f0, t, ap
93 |
94 |
95 | @njit
96 | def cumulative_mean_normalized_difference_function(frame, lag_max):
97 | """
98 | Computes Cumulative Mean Normalized Difference Function (CMNDF).
99 |
100 | Parameters
101 | ----------
102 | frame : ndarray
103 | Audio frame
104 | lag_max : int
105 | Maximum expected lag in the CMNDF
106 |
107 | Returns
108 | -------
109 | cmndf : ndarray
110 | Cumulative Mean Normalized Difference Function
111 | """
112 |
113 | cmndf = np.zeros(lag_max+1) # Initialize CMNDF
114 | cmndf[0] = 1
115 | diff_mean = 0
116 |
117 | for tau in range(1, lag_max+1):
118 | # Difference function
119 | diff = np.sum((frame[0:-tau] - frame[0 + tau:]) ** 2)
120 | # Iterative mean of the difference function
121 | diff_mean = diff_mean*(tau-1)/tau + diff/tau
122 |
123 | cmndf[tau] = diff / (diff_mean + np.finfo(np.float64).eps)
124 |
125 | return cmndf
126 |
127 |
128 | def absolute_thresholding(cmndf, threshold, lag_min, lag_max, parabolic_interp=True):
129 | """
130 | Absolute thresholding:
131 | Set an absolute threshold and choose the smallest value of tau that gives a minimum of d' deeper than that
132 | threshold. If none is found, the global minimum is chosen instead.
133 |
134 | Parameters
135 | ----------
136 | cmndf : ndarray
137 | Cumulative Mean Normalized Difference Function
138 | threshold : float
139 | Threshold
140 | lag_min : float
141 | Minimal lag
142 | lag_max : float
143 | Maximal lag
144 | parabolic_interp : bool
145 | Switch to activate/deactivate parabolic interpolation
146 |
147 | Returns
148 | -------
149 |
150 | """
151 |
152 | # take shortcut if search range only allows for one possible lag
153 | if lag_min == lag_max:
154 | return lag_min
155 |
156 | # find local minima below absolute threshold in interval [lag_min:lag_max]
157 | local_min_idxs = (np.argwhere((cmndf[1:-1] < cmndf[0:-2]) & (cmndf[1:-1] < cmndf[2:]))).flatten() + 1
158 | below_thr_idxs = np.argwhere(cmndf[lag_min:lag_max] < threshold).flatten() + lag_min
159 | # numba compatible intersection of indices sets
160 | min_idxs = np.unique(np.array([i for i in local_min_idxs for j in below_thr_idxs if i == j]))
161 |
162 | # if no local minima below threshold are found, return global minimum
163 | if not min_idxs.size:
164 | return np.argmin(cmndf[lag_min:lag_max]) + lag_min
165 |
166 | # find first local minimum
167 | lag = np.min(min_idxs) # choose first local minimum
168 |
169 | # Optional: Parabolic Interpolation of local minima
170 | if parabolic_interp:
171 | lag_corr, cmndf[lag] = parabolic_interpolation(cmndf[lag-1], cmndf[lag], cmndf[lag+1])
172 | lag += lag_corr
173 |
174 | return lag
175 |
176 |
177 | @njit
178 | def parabolic_interpolation(y1, y2, y3):
179 | """
180 | Parabolic interpolation of an extremal value given three samples with equal spacing on the x-axis.
181 | The middle value y2 is assumed to be the extremal sample of the three.
182 |
183 | Parameters
184 | ----------
185 | y1: f(x1)
186 | y2: f(x2)
187 | y3: f(x3)
188 |
189 | Returns
190 | -------
191 | x_interp: Interpolated x-value (relative to x3-x2)
192 | y_interp: Interpolated y-value, f(x_interp)
193 | """
194 |
195 | a = np.finfo(np.float64).eps + (y1 + y3 - 2 * y2) / 2
196 | b = (y3 - y1) / 2
197 | x_interp = -b / (2 * a)
198 | y_interp = y2 - (b ** 2) / (4 * a)
199 |
200 | return x_interp, y_interp
201 |
202 |
203 | def aperiodicity(frame, lag_est):
204 | """
205 | Compute aperiodicity of given frame (serves as indicator for reliability or voicing detection).
206 |
207 | Parameters
208 | ----------
209 | frame : ndarray
210 | Frame
211 | lag_est : float
212 | Estimated lag
213 |
214 | Returns
215 | -------
216 | ap: float
217 | Aperiodicity (the lower, the more reliable the estimate)
218 | """
219 |
220 | lag_int = int(np.floor(lag_est)) # uncorrected period estimate
221 | frac = lag_est - lag_int # residual
222 |
223 | # Pad frame to insure constant size
224 | frame_pad = np.concatenate((frame, np.flip(frame))) # mirror padding
225 |
226 | # Shift frame by estimated period
227 | if frac == 0:
228 | frame_shift = frame_pad[lag_int:lag_int+len(frame)]
229 | else:
230 | # linear interpolation between adjacent shifts
231 | frame_shift = (1 - frac) * frame_pad[lag_int:lag_int+len(frame)] + \
232 | frac * frame_pad[lag_int+1:lag_int+1+len(frame)]
233 |
234 | pwr = (np.mean(frame ** 2) + np.mean(frame_shift ** 2)) / 2 # average power over fixed and shifted frame
235 | res = np.mean((frame - frame_shift) ** 2) / 2 # residual power
236 | ap = res / (pwr + np.finfo(np.float64).eps)
237 |
238 | return ap
239 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 |
4 | with open('README.md', 'r') as fdesc:
5 | long_description = fdesc.read()
6 |
7 | setup(
8 | name='libf0',
9 | version='1.0.2',
10 | description='A Python Library for Fundamental Frequency Estimation in Music Recordings',
11 | author='Sebastian Rosenzweig, Simon Schwär, and Meinard Müller',
12 | author_email='meinard.mueller@audiolabs-erlangen.de',
13 | url='https://github.com/groupmm/libf0',
14 | download_url='https://github.com/groupmm/libf0',
15 | packages=find_packages(),
16 | long_description=long_description,
17 | long_description_content_type='text/markdown',
18 | classifiers=[
19 | "License :: OSI Approved :: MIT License",
20 | "Programming Language :: Python",
21 | "Intended Audience :: Developers",
22 | "Topic :: Multimedia :: Sound/Audio :: Analysis",
23 | "Programming Language :: Python :: 3",
24 | ],
25 | keywords=['audio', 'music', 'f0', 'pitch', 'yin', 'pyin', 'melodia', 'swipe'],
26 | license='MIT',
27 | install_requires=[
28 | 'librosa >= 0.8.0, < 1.0.0',
29 | 'numba >= 0.51.0, < 1.0.0',
30 | 'numpy >= 1.17.0, < 2.0.0',
31 | 'scipy >= 1.3.0, < 2.0.0'],
32 | python_requires='>=3.6',
33 | extras_require={
34 | 'dev': ['ipython >= 7.8.0, < 8.0.0',
35 | 'jupyter == 1.0.*',
36 | 'matplotlib >= 3.1.0, < 4.0.0',
37 | 'nbstripout == 0.4.*',
38 | 'pysoundfile >= 0.9.0, < 1.0.0'],
39 | 'tests': ['pytest == 6.2.*'],
40 | 'docs': ['sphinx == 4.0.*',
41 | 'sphinx-rtd-theme == 0.5.*'],
42 | }
43 | )
44 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/tests/__init__.py
--------------------------------------------------------------------------------
/tests/__pycache__/conftest.cpython-38-pytest-7.1.2.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupmm/libf0/0baf532b506e2489fd7abebcf229a3c7d9cb98b7/tests/__pycache__/conftest.cpython-38-pytest-7.1.2.pyc
--------------------------------------------------------------------------------
/tests/test_algorithms.py:
--------------------------------------------------------------------------------
1 | """
2 | | Description: Tests for functions of libf0
3 | | Contributors: Sebastian Rosenzweig, Meinard Müller
4 | | License: The MIT license, https://opensource.org/licenses/MIT
5 | | This file is part of libf0.
6 | """
7 |
8 | import numpy as np
9 | from scipy.signal import chirp
10 | from scipy.interpolate import interp1d
11 | import libf0
12 |
13 |
14 | # Generate test audio 1: Sinusoid
15 | Fs = 22050 # Sampling rate
16 | dur_sec = 5 # Duration of generated audio
17 | T_audio = np.arange(0, dur_sec*Fs) / Fs # Audio time axis
18 | F_sine = 440 # Frequency of sinusoid
19 | x1 = np.sin(2*np.pi*F_sine*T_audio) # Sinusoidal signal
20 |
21 | # Generate test audio 2: Sweep
22 | F_start_chirp = 220.0
23 | F_end_chirp = 640.0
24 | x2 = chirp(T_audio, F_start_chirp, dur_sec, F_end_chirp, method='logarithmic')
25 |
26 | F0_chirp = F_start_chirp * (F_end_chirp / F_start_chirp) ** (T_audio / dur_sec) # Ground truth F0
27 | chirp_func = interp1d(T_audio, F0_chirp, kind='cubic') # interpolation function to make outputs comparable
28 |
29 | # Further (global) parameters
30 | F_min = 55.0 # Minimum frequency
31 | F_max = 1760.0 # Maximum frequency
32 | N = 2048 # Window size
33 | H = 256 # Hop size
34 | atol_sine = 10 # absolute test tolerance for sine signal in cents, tolerance for chirp varies depending on algorithm
35 |
36 |
37 | def test_hz_to_cents():
38 | F_cents = libf0.hz_to_cents(440, F_ref=55.0)
39 | assert F_cents == 3600
40 |
41 |
42 | def test_cents_to_hz():
43 | F_hz = libf0.cents_to_hz(3600, F_ref=55.0)
44 | assert F_hz == 440
45 |
46 |
47 | def test_yin():
48 | threshold = 0.15
49 | f0_yin_x1, t_yin_x1, conf_yin_x1 = libf0.yin(x1, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, threshold=threshold)
50 | f0_yin_x2, t_yin_x2, conf_yin_x2 = libf0.yin(x2, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, threshold=threshold)
51 |
52 | assert np.allclose(libf0.hz_to_cents(f0_yin_x1), libf0.hz_to_cents(F_sine), rtol=0, atol=atol_sine)
53 | assert np.allclose(libf0.hz_to_cents(f0_yin_x2), libf0.hz_to_cents(chirp_func(t_yin_x2)), rtol=0, atol=50)
54 |
55 |
56 | def test_pyin():
57 | R = 10
58 | thrs = np.arange(0.01, 1, 0.01)
59 | betas = [1, 18]
60 | abs_min_prob = 0.01
61 | voicing_prob = 0.5
62 | f0_pyin_x1, t_pyin_x1, conf_pyin_x1 = libf0.pyin(x1, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, R=R,
63 | thresholds=thrs, beta_params=betas,
64 | absolute_min_prob=abs_min_prob, voicing_prob=voicing_prob)
65 | f0_pyin_x2, t_pyin_x2, conf_pyin_x2 = libf0.pyin(x2, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, R=R,
66 | thresholds=thrs, beta_params=betas,
67 | absolute_min_prob=abs_min_prob, voicing_prob=voicing_prob)
68 |
69 | # exclude first frame, since it is always 0
70 | assert np.allclose(libf0.hz_to_cents(f0_pyin_x1[1:]), libf0.hz_to_cents(F_sine), rtol=0, atol=atol_sine)
71 | assert np.allclose(libf0.hz_to_cents(f0_pyin_x2[1:]), libf0.hz_to_cents(chirp_func(t_pyin_x2[1:])), rtol=0, atol=20)
72 |
73 |
74 | def test_salience():
75 | R = 10
76 | n_harm = 10
77 | freq_smooth_len = 11
78 | alpha = 0.9
79 | gamma = 0.0
80 | tol = 5
81 | score_low = 0.01
82 | score_high = 1.0
83 | f0_sal_x1, t_sal_x1, conf_sal_x1 = libf0.salience(x1, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, R=R,
84 | num_harm=n_harm, freq_smooth_len=freq_smooth_len, alpha=alpha,
85 | gamma=gamma, constraint_region=None, tol=tol,
86 | score_low=score_low, score_high=score_high)
87 |
88 | f0_sal_x2, t_sal_x2, conf_sal_x2 = libf0.salience(x2, Fs=Fs, N=N, H=H, F_min=F_min, F_max=F_max, R=R,
89 | num_harm=n_harm, freq_smooth_len=freq_smooth_len, alpha=alpha,
90 | gamma=gamma, constraint_region=None, tol=tol,
91 | score_low=score_low, score_high=score_high)
92 |
93 | assert np.allclose(libf0.hz_to_cents(f0_sal_x1), libf0.hz_to_cents(F_sine), rtol=0, atol=atol_sine)
94 | assert np.allclose(libf0.hz_to_cents(f0_sal_x2), libf0.hz_to_cents(chirp_func(t_sal_x2)), rtol=0, atol=20)
95 |
96 |
97 | def test_swipe():
98 | dlog2p = 1 / 96
99 | derbs = 0.1
100 | strength_threshold = 0
101 | f0_swipe_x1, t_swipe_x1, conf_swipe_x1 = libf0.swipe(x1, Fs=Fs, H=H, F_min=F_min, F_max=F_max, dlog2p=dlog2p,
102 | derbs=derbs, strength_threshold=strength_threshold)
103 | f0_swipe_x2, t_swipe_x2, conf_swipe_x2 = libf0.swipe(x2, Fs=Fs, H=H, F_min=F_min, F_max=F_max, dlog2p=dlog2p,
104 | derbs=derbs, strength_threshold=strength_threshold)
105 |
106 | # exclude first an last frame due to artifacts at signal edges
107 | assert np.allclose(libf0.hz_to_cents(f0_swipe_x1[1:-1]), libf0.hz_to_cents(F_sine), rtol=0, atol=atol_sine)
108 | assert np.allclose(libf0.hz_to_cents(f0_swipe_x2[1:-1]), libf0.hz_to_cents(chirp_func(t_swipe_x2[1:-1])), rtol=0,
109 | atol=50)
110 |
111 |
112 | def test_swipe_slim():
113 | R = 10
114 | strength_threshold = 0
115 | f0_swipes_x1, t_swipes_x1, conf_swipes_x1 = libf0.swipe_slim(x1, Fs=Fs, H=H, F_min=F_min, F_max=F_max, R=R,
116 | strength_threshold=strength_threshold)
117 | f0_swipes_x2, t_swipes_x2, conf_swipes_x2 = libf0.swipe_slim(x2, Fs=Fs, H=H, F_min=F_min, F_max=F_max, R=R,
118 | strength_threshold=strength_threshold)
119 |
120 | # exclude first an last frame due to artifacts at signal edges
121 | assert np.allclose(libf0.hz_to_cents(f0_swipes_x1[1:-1]), libf0.hz_to_cents(F_sine), rtol=0, atol=atol_sine)
122 | assert np.allclose(libf0.hz_to_cents(f0_swipes_x2[1:-1]), libf0.hz_to_cents(chirp_func(t_swipes_x2[1:-1])), rtol=0,
123 | atol=20)
124 |
--------------------------------------------------------------------------------