diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..92f585867 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +source = + ansys.pydpf_sound + +[report] +show_missing = true diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..ac415d535 --- /dev/null +++ b/.flake8 @@ -0,0 +1,7 @@ +[flake8] +exclude = venv, __init__.py, doc/_build, .venv, doc/source/conf.py +select = W191, W291, W293, W391, E115, E117, E122, E124, E125, E225, E231, E301, E303, E501, F401, F403 +count = True +max-complexity = 10 +max-line-length = 100 +statistics = True diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..dccedadfe --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto !eol +*.sh eol=lf +*.bat eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..0b6898451 --- /dev/null +++ b/.gitignore @@ -0,0 +1,168 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.cov +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +doc/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv* +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# End of https://www.toptal.com/developers/gitignore/api/python + +# Auto-generated doc files +doc/source/sg_execution_times.rst +doc/source/examples/gallery_examples +doc/source/api/_autosummary/ +*flute_modified.wav + +# VSCode +.vscode/ \ No newline at end of file diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..8e5f4ad4d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +repos: + +- repo: https://github.com/psf/black + rev: 23.1.0 + hooks: + - id: black + +- repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + +- repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + +- repo: https://github.com/codespell-project/codespell + rev: v2.2.4 + hooks: + - id: codespell + exclude: Docker + +- repo: https://github.com/pycqa/pydocstyle + rev: 6.3.0 + hooks: + - id: pydocstyle + additional_dependencies: [toml] + exclude: "^(tests/|examples/)" + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-merge-conflict + - id: trailing-whitespace + +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.22.0 + hooks: + - id: check-github-workflows diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..b8c337015 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,12 @@ +# This is the list of pydpf-sound's significant contributors. +# +# This file does not necessarily list everyone who has contributed code. +# +# For contributions made under a Corporate CLA, the organization is +# added to this file. +# +# If you have contributed to the repository and wish to be added to this file +# please submit a request. +# +# +ANSYS, Inc. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..505d21df1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +# CHANGELOG \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..0a9033fc0 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +sound.docs.pyansys.com diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..1cf484f16 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,65 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our +project and our community a harassment-free experience for everyone, +regardless of age, body size, disability, ethnicity, sex +characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual + attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..8ba1f31cb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,2 @@ +# Contributing + diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 000000000..69b534798 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,9 @@ +# Contributors + +## Project Lead or Owner + +* [First Last]() + +## Individual Contributors + +* [First Last]() diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..69af3d3e4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 ANSYS, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..b37cccec4 --- /dev/null +++ b/README.rst @@ -0,0 +1,176 @@ +**************** +PyDPF Sound +**************** + +|pyansys| |python| |GH-CI| |codecov| |MIT| |black| + +.. |pyansys| image:: https://img.shields.io/badge/Py-Ansys-ffc107.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABDklEQVQ4jWNgoDfg5mD8vE7q/3bpVyskbW0sMRUwofHD7Dh5OBkZGBgW7/3W2tZpa2tLQEOyOzeEsfumlK2tbVpaGj4N6jIs1lpsDAwMJ278sveMY2BgCA0NFRISwqkhyQ1q/Nyd3zg4OBgYGNjZ2ePi4rB5loGBhZnhxTLJ/9ulv26Q4uVk1NXV/f///////69du4Zdg78lx//t0v+3S88rFISInD59GqIH2esIJ8G9O2/XVwhjzpw5EAam1xkkBJn/bJX+v1365hxxuCAfH9+3b9/+////48cPuNehNsS7cDEzMTAwMMzb+Q2u4dOnT2vWrMHu9ZtzxP9vl/69RVpCkBlZ3N7enoDXBwEAAA+YYitOilMVAAAAAElFTkSuQmCC + :target: https://docs.pyansys.com/ + :alt: PyAnsys + +.. |python| image:: https://img.shields.io/badge/Python-%3E%3D3.9-blue + :target: https://pypi.org/project/ansys-dpf-composites/ + :alt: Python + +.. |codecov| image:: https://codecov.io/gh/ansys-internal/pydpf-sound/branch/main/graph/badge.svg + :target: https://codecov.io/gh/ansys-internal/pydpf-sound/ + :alt: Codecov + +.. |GH-CI| image:: https://github.com/ansys-internal/pydpf-sound/actions/workflows/ci_cd.yml/badge.svg + :target: https://github.com/ansys-internal/pydpf-sound/actions/workflows/ci_cd.yml + :alt: GH-CI + +.. |MIT| image:: https://img.shields.io/badge/License-MIT-yellow.svg + :target: https://opensource.org/licenses/MIT + :alt: MIT + +.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=flat + :target: https://github.com/psf/black + :alt: Black + + +.. index_start + +PyDPF Sound enables the post-processing and analysis of sounds based on +`Ansys DPF`_ and the DPF Sound plugin. It is a Python wrapper which +implements classes on top of DPF Sound operators. For +information demonstrating the behavior and usage of PyDPF Sound, +see Examples in the DPF Sound documentation. + +.. START_MARKER_FOR_SPHINX_DOCS + +---------- +Contribute +---------- + + +A Python wrapper for Ansys DPF Sound library. + + +How to install +-------------- + +Two installation modes are provided: user and developer. + +For users +^^^^^^^^^ + +User installation can be performed by running: + +.. code:: bash + + python -m pip install ansys-pydpf-sound + +For developers +^^^^^^^^^^^^^^ + +Installing the Pydpf-sound Library in developer mode allows +you to modify the source and enhance it. + +Before contributing to the project, please refer to the `PyAnsys Developer's guide`_. You will +need to follow these steps: + +#. Start by cloning this repository: + + .. code:: bash + + git clone https://github.com/ansys/pydpf-sound + +#. Create a fresh-clean Python environment and activate it. Refer to the + official `venv`_ documentation if you require further information: + + .. code:: bash + + # Create a virtual environment + python -m venv .venv + + # Activate it in a POSIX system + source .venv/bin/activate + + # Activate it in Windows CMD environment + .venv\Scripts\activate.bat + + # Activate it in Windows Powershell + .venv\Scripts\Activate.ps1 + +#. Make sure you have the latest version of `pip`_: + + .. code:: bash + + python -m pip install -U pip + +#. Install the project in editable mode: + + .. code:: bash + + python -m pip install --editable ansys-pydpf-sound + +#. Install additional requirements (if needed): + + .. code:: bash + + python -m pip install -r requirements/requirements_build.txt + python -m pip install -r requirements/requirements_doc.txt + python -m pip install -r requirements/requirements_tests.txt + +#. Finally, verify your development installation by running: + + .. code:: bash + + python -m pip install -r requirements/requirements_tests.txt + pytest tests -v + + +Style and Testing +----------------- + +If required, you can always call the style (`black`_, `isort`_, +`flake8`_...) or unit testing (`pytest`_) commands from the command line. However, +this does not guarantee that your project is being tested in an isolated +environment, which is another reason to consider using `tox`_. + + +Documentation +------------- + +For building documentation, you can run the usual rules provided in the +`Sphinx`_ makefile, such as: + +.. code:: bash + + python -m pip install -r requirements/requirements_doc.txt + make -C doc/ html + + # subsequently open the documentation with (under Linux): + your_browser_name doc/html/index.html + +Distributing +------------ + +If you would like to create either source or wheel files, start by installing +the building requirements: + +.. code:: bash + + python -m pip install -r requirements/requirements_build.txt + +Then, you can execute: + +.. code:: bash + + python -m build + python -m twine check dist/* + + +.. LINKS AND REFERENCES +.. _black: https://github.com/psf/black +.. _flake8: https://flake8.pycqa.org/en/latest/ +.. _isort: https://github.com/PyCQA/isort +.. _PyAnsys Developer's guide: https://dev.docs.pyansys.com/ +.. _pre-commit: https://pre-commit.com/ +.. _pytest: https://docs.pytest.org/en/stable/ +.. _Sphinx: https://www.sphinx-doc.org/en/master/ +.. _pip: https://pypi.org/project/pip/ +.. _tox: https://tox.wiki/ +.. _venv: https://docs.python.org/3/library/venv.html +.. _Ansys DPF: https://dpf.docs.pyansys.com/version/stable/ diff --git a/doc/.vale.ini b/doc/.vale.ini new file mode 100644 index 000000000..2aeb2ed82 --- /dev/null +++ b/doc/.vale.ini @@ -0,0 +1,31 @@ +# Core settings +# ============= + +# Location of our `styles` +StylesPath = "styles" + +# The options are `suggestion`, `warning`, or `error` (defaults to “warning”). +MinAlertLevel = warning + +# By default, `code` and `tt` are ignored. +IgnoredScopes = code, tt + +# By default, `script`, `style`, `pre`, and `figure` are ignored. +SkippedScopes = script, style, pre, figure + +# WordTemplate specifies what Vale will consider to be an individual word. +WordTemplate = \b(?:%s)\b + +# List of Packages to be used for our guidelines +Packages = Google + +# Define the Ansys vocabulary +Vocab = ANSYS + +[*.{md,rst}] + +# Apply the following styles +BasedOnStyles = Vale, Google + +# Removing google specific rule as it creates false positives +Google.Headings = NO \ No newline at end of file diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..fd967771a --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,32 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -j auto +SPHINXBUILD = sphinx-build +SOURCEDIR = source +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + + +# Customized clean due to examples gallery +clean: + rm -rf $(BUILDDIR)/* + rm -rf $(SOURCEDIR)/examples + find . -type d -name "_autosummary" -exec rm -rf {} + + +# Customized pdf for svg format images +pdf: + @$(SPHINXBUILD) -M latex "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + cd $(BUILDDIR)/latex && latexmk -r latexmkrc -pdf *.tex -interaction=nonstopmode || true + (test -f $(BUILDDIR)/latex/*.pdf && echo pdf exists) || exit 1 diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 000000000..c315a88f0 --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,56 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=_build +set SPHINXOPTS=-j auto -W + +if "%1" == "" goto help +if "%1" == "clean" goto clean +if "%1" == "pdf" goto pdf +if "%1" == "html" goto html + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:html +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:clean +rmdir /s /q %BUILDDIR% > /NUL 2>&1 +for /d /r %SOURCEDIR% %%d in (_autosummary) do @if exist "%%d" rmdir /s /q "%%d" +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:pdf +%SPHINXBUILD% -M latex %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +cd "%BUILDDIR%\latex" +for %%f in (*.tex) do ( +pdflatex "%%f" --interaction=nonstopmode) +if NOT EXIST ansys-dpf-sound-*.pdf ( + Echo "no pdf generated!" + exit /b 1) +Echo "pdf generated!" +goto end + +:end +popd diff --git a/doc/source/_static/README.md b/doc/source/_static/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/doc/source/_templates/README.md b/doc/source/_templates/README.md new file mode 100644 index 000000000..86a233caa --- /dev/null +++ b/doc/source/_templates/README.md @@ -0,0 +1 @@ +## Contains templates for the documentation build diff --git a/doc/source/api/index.rst b/doc/source/api/index.rst new file mode 100644 index 000000000..8bec28f75 --- /dev/null +++ b/doc/source/api/index.rst @@ -0,0 +1,13 @@ +API reference +============= + +This section describes the public classes, methods, and attributes of the PyDPF Sound API. + +.. module:: ansys.dpf.sound + +.. toctree:: + :maxdepth: 2 + + signal_utilities + spectrogram_processing + psychoacoustics \ No newline at end of file diff --git a/doc/source/api/psychoacoustics.rst b/doc/source/api/psychoacoustics.rst new file mode 100644 index 000000000..23a959f29 --- /dev/null +++ b/doc/source/api/psychoacoustics.rst @@ -0,0 +1,13 @@ +Psychoacoustics +--------------- + +.. module:: ansys.dpf.sound.psychoacoustics + +.. autosummary:: + :toctree: _autosummary + + LoudnessISO532_1_Stationary + LoudnessISO532_1_TimeVarying + Sharpness + Roughness + FluctuationStrength diff --git a/doc/source/api/signal_utilities.rst b/doc/source/api/signal_utilities.rst new file mode 100644 index 000000000..6159f7c62 --- /dev/null +++ b/doc/source/api/signal_utilities.rst @@ -0,0 +1,16 @@ +Signal utilities +---------------- + +.. module:: ansys.dpf.sound.signal_utilities + +.. autosummary:: + :toctree: _autosummary + + ApplyGain + CreateSoundField + CropSignal + LoadWav + Resample + SumSignals + WriteWav + ZeroPad \ No newline at end of file diff --git a/doc/source/api/spectrogram_processing.rst b/doc/source/api/spectrogram_processing.rst new file mode 100644 index 000000000..c8d14c024 --- /dev/null +++ b/doc/source/api/spectrogram_processing.rst @@ -0,0 +1,11 @@ +Spectrogram Processing +---------------------- + +.. module:: ansys.dpf.sound.spectrogram_processing + +.. autosummary:: + :toctree: _autosummary + + Stft + Istft + IsolateOrders \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 000000000..3486c5520 --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,170 @@ +"""Sphinx documentation configuration file.""" +from datetime import datetime +import os + +from ansys_sphinx_theme import get_version_match +from ansys_sphinx_theme import pyansys_logo_black as logo +import numpy as np +import pyvista +from pyvista.plotting.utilities.sphinx_gallery import DynamicScraper +from sphinx_gallery.sorting import FileNameSortKey + +from ansys.dpf.sound import __version__ + +# Manage errors +pyvista.set_error_output_file("errors.txt") + +# Ensure that offscreen rendering is used for docs generation +pyvista.OFF_SCREEN = True + +# necessary when building the sphinx gallery +pyvista.BUILDING_GALLERY = True + +pyvista.global_theme.window_size = np.array([1024, 768]) * 2 + +# Project information +project = "ansys-dpf-sound" +copyright = f"(c) {datetime.now().year} ANSYS, Inc. All rights reserved" +author = "ANSYS, Inc." +release = version = __version__ +cname = os.getenv("DOCUMENTATION_CNAME", "docs.pyansys.com") + +# Select desired logo, theme, and declare the html title +html_logo = logo +html_theme = "ansys_sphinx_theme" +html_short_title = html_title = "PyDPF Sound" + +# specify the location of your github repo +html_theme_options = { + "github_url": "https://github.com/ansys/pydpf-sound", + "show_prev_next": False, + "show_breadcrumbs": True, + "additional_breadcrumbs": [ + ("PyAnsys", "https://docs.pyansys.com/"), + ], + "switcher": { + "json_url": f"https://{cname}/versions.json", + "version_match": get_version_match(__version__), + }, + "check_switcher": False, +} + +# Sphinx extensions +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "numpydoc", + "sphinx.ext.intersphinx", + "sphinx_copybutton", + "sphinx.ext.napoleon", + "sphinx_autodoc_typehints", + "sphinx_gallery.gen_gallery", + "sphinx_design", + "pyvista.ext.viewer_directive", +] + +# Intersphinx mapping +intersphinx_mapping = { + "python": ("https://docs.python.org/dev", None), + "ansys-dpf-core": ("https://dpf.docs.pyansys.com/version/stable", None), + "numpy": ("https://numpy.org/doc/stable", None), + "matplotlib": ("https://matplotlib.org/stable", None), + # kept here as an example + # "scipy": ("https://docs.scipy.org/doc/scipy/reference", None), + # "numpy": ("https://numpy.org/devdocs", None), + # "matplotlib": ("https://matplotlib.org/stable", None), + # "pandas": ("https://pandas.pydata.org/pandas-docs/stable", None), + # "pyvista": ("https://docs.pyvista.org/", None), + # "grpc": ("https://grpc.github.io/grpc/python/", None), +} + +# numpydoc configuration +numpydoc_show_class_members = False +numpydoc_xref_param_type = True + +# Consider enabling numpydoc validation. See: +# https://numpydoc.readthedocs.io/en/latest/validation.html# +numpydoc_validate = True +numpydoc_validation_checks = { + "GL06", # Found unknown section + "GL07", # Sections are in the wrong order. + # "GL08", # The object does not have a docstring + "GL09", # Deprecation warning should precede extended summary + "GL10", # reST directives {directives} must be followed by two colons + "SS01", # No summary found + "SS02", # Summary does not start with a capital letter + # "SS03", # Summary does not end with a period + "SS04", # Summary contains heading whitespaces + # "SS05", # Summary must start with infinitive verb, not third person + "RT02", # The first line of the Returns section should contain only the + # type, unless multiple values are being returned" +} + +# sphinx gallery options +sphinx_gallery_conf = { + # convert rst to md for ipynb + "pypandoc": True, + # path to your examples scripts + "examples_dirs": ["../../examples"], + # path where to save gallery generated examples + "gallery_dirs": ["examples/gallery_examples"], + # Pattern to search for example files + "filename_pattern": r"\.py", + # Remove the "Download all examples" button from the top level gallery + "download_all_examples": False, + # Sort gallery example by file name instead of number of lines (default) + "within_subsection_order": FileNameSortKey, + # directory where function granular galleries are stored + "backreferences_dir": None, + # Modules for which function level galleries are created. In + "doc_module": "ansys-dpf-sound", + "image_scrapers": (DynamicScraper(), "matplotlib"), + "ignore_pattern": r"__init__\.py", + "thumbnail_size": (350, 350), +} + +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + # because we include this in examples/index.rst + "examples/gallery_examples/index.rst", + "*convert_fields_container_to_np_array.rst", +] + + +# static path +html_static_path = ["_static"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix(es) of source filenames. +source_suffix = ".rst" + +# The master toctree document. +master_doc = "index" + +# -- Options for LaTeX output ------------------------------------------------ +latex_elements = {} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( + master_doc, + f"{project}-Documentation-{__version__}.tex", + f"{project} Documentation", + author, + "manual", + ), +] + +# Fix to resolve hyperlink warnings when building PDF +# ( https://stackoverflow.com/questions/67485567/sphinx-cross-reference-in-latex) +latex_elements = { + "preamble": r""" +\renewcommand{\hyperref}[2][]{#2} +""" +} diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst new file mode 100644 index 000000000..a0bbfc4bd --- /dev/null +++ b/doc/source/examples/index.rst @@ -0,0 +1,9 @@ +.. _ref_examples: + +.. === EXAMPLES Gallery === + +.. + We have to include this rather than include it in a tree. + +.. include:: gallery_examples/index.rst + :start-line: 2 \ No newline at end of file diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst new file mode 100644 index 000000000..100baad2b --- /dev/null +++ b/doc/source/getting_started.rst @@ -0,0 +1,63 @@ +Getting started +--------------- + +Installation +^^^^^^^^^^^^ + +PyDPF Sound supports Ansys version 2024 R1 and later. Make sure you have a licensed copy of Ansys installed. See +:ref:`Compatibility` to understand which ``ansys-dpf-sound`` version corresponds to which Ansys version. + +Install the ``ansys-dpf-sound`` package with ``pip``: + +.. code:: + + pip install ansys-dpf-sound + +Specific versions can be installed by specifying the version in the pip command. For example: Ansys 2024 R1 requires ansys-dpf-sound version 0.1.0: + +.. code:: + + pip install ansys-dpf-sound==0.1.0 + + +You should use a `virtual environment `_, +because it keeps Python packages isolated from your system Python. + + +Examples +^^^^^^^^ + +The :doc:`examples/index` section provides these basic examples for getting started: + +* :ref:`sphx_glr_examples_gallery_examples_001_load_write_wav_files.py` + +At the end of each example, there is a button for downloading the example's Python source code. +Input files, such as the input wav files, are downloaded from a Git +repository when running the example. + + +.. _Compatibility: + +Compatibility +""""""""""""" + +The following table shows which ``ansys-dpf-sound`` version is compatible with which server version (Ansys version). See :ref:`Get DPF Sound Prerelease` to get the pre-releases. +By default, the DPF server is started from the latest Ansys installer. + +.. list-table:: + :widths: 20 20 + :header-rows: 1 + + * - Server version + - ansys.dpf.sound Python module version + * - 8.0 (Ansys 2024 R2 pre0) + - 0.1.0 and later + + +.. _Get DPF Sound Prerelease : + +Getting the DPF server docker image +""""""""""""""""""""""""""""""""""" +Follow the steps described in the DPF documentation in the `Run DPF Server in A Docker Container `_ section. +Make sure you also download the composites plugin (e.g ``ansys_dpf_sound_win_v2024.1.pre0.zip``). +After following the preceding steps, you should have a running DPF docker container that listens on port 50052. diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 000000000..5e850b839 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,15 @@ +.. + Just reuse the root readme to avoid duplicating the documentation. + Provide any documentation specific to your online documentation + here. +.. toctree:: + :hidden: + :maxdepth: 3 + + self + getting_started + api/index + examples/index + +.. include:: ../../README.rst + :start-after: .. index_start diff --git a/doc/styles/.gitignore b/doc/styles/.gitignore new file mode 100644 index 000000000..080f12aa4 --- /dev/null +++ b/doc/styles/.gitignore @@ -0,0 +1,4 @@ +* +!Vocab +!Vocab/** +!.gitignore \ No newline at end of file diff --git a/doc/styles/Vocab/ANSYS/accept.txt b/doc/styles/Vocab/ANSYS/accept.txt new file mode 100644 index 000000000..df18af6bd --- /dev/null +++ b/doc/styles/Vocab/ANSYS/accept.txt @@ -0,0 +1,9 @@ +ANSYS +Ansys +ansys +Wav +wav +Psychoacoustic +psychoacoustic +Psychoacoustics +psychoacoustics \ No newline at end of file diff --git a/doc/styles/Vocab/ANSYS/reject.txt b/doc/styles/Vocab/ANSYS/reject.txt new file mode 100644 index 000000000..e69de29bb diff --git a/docker/Dockerfile.windows b/docker/Dockerfile.windows new file mode 100644 index 000000000..5a2e48987 --- /dev/null +++ b/docker/Dockerfile.windows @@ -0,0 +1,64 @@ +# syntax=docker/dockerfile:1 +# escape=` + +ARG BASE_IMAGE=mcr.microsoft.com/windows:ltsc2019 +ARG DPF_PACKAGE_VERSION=24.2.pre0 +ARG DPF_PACKAGE_VERSION_NO_DOTS=2024_2_pre0 + +########################################## +# BASE WITH VISUAL STUDIO TOOLS # +########################################## +FROM $BASE_IMAGE AS libraries_installer + +ARG BASE_IMAGE + +# Set LABEL org.opencontainers.image.created with the command line, use: +# Powershell : Get-Date -Format o | ForEach-Object { $_ -replace ":", "." } +# bash: $(date --utc +%FT%TZ) +LABEL org.opencontainers.image.title="ANSYS DPF Sound" +LABEL org.opencontainers.image.description="Windows container image with ANSYS DPF and ANSYS DPF Sound plugin." +LABEL org.opencontainers.image.base.name=$BASE_IMAGE + +# Set the default Windows shell for correct batch processing. +SHELL ["cmd", "/S", "/C"] + +WORKDIR C:\\ + +RUN ` + # Download the Build Tools bootstrapper. + curl -SL --output vs_redist.exe https://aka.ms/vs/17/release/vc_redist.x64.exe ` + ` + # Install Build Tools with the Microsoft.VisualStudio.Workload.AzureBuildTools workload, excluding workloads and components with known issues. + && (start /w vs_redist.exe /quiet /norestart /install) ` + ` + # Cleanup + && del /q vs_redist.exe + + +########################################## +# DPF SERVER # +########################################## +FROM libraries_installer + +ARG DPF_PACKAGE_VERSION +ARG DPF_PACKAGE_VERSION_NO_DOTS + +LABEL com.ansys/dpf.version=$DPF_PACKAGE_VERSION + +EXPOSE 50052 + +SHELL ["cmd", "/S", "/C"] + +# Setting DPF Sound folder path in the environment variables +ENV DPF_PACKAGE_VERSION_NO_DOTS=$DPF_PACKAGE_VERSION_NO_DOTS +RUN setx /m PATH "%PATH%;C:\\ansys\\dpf\\server_%DPF_PACKAGE_VERSION_NO_DOTS%\\Acoustics\\SAS\\ads" + +# Copying all the files from ansys to C:\ansys in the container +COPY ansys C:\\ansys + +# Working directory +WORKDIR C:\\ansys\\dpf\\server_${DPF_PACKAGE_VERSION_NO_DOTS}\\aisol\\bin\\winx64\\ + +# Define the entry point for the docker container. +# This entry point starts the developer command prompt and launches the PowerShell shell. +ENTRYPOINT ["cmd", "/S", "/C", ".\\Ans.Dpf.Grpc.bat"] diff --git a/examples/001_load_write_wav_files.py b/examples/001_load_write_wav_files.py new file mode 100644 index 000000000..293f12c5a --- /dev/null +++ b/examples/001_load_write_wav_files.py @@ -0,0 +1,106 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. + +""" +.. _load_resample_amplify_write_wav_files_example: + +Load / Write Wav Files, resample and apply gains +------------------------------------------------ + +This example shows how to load a Wav file, modify its samply frequency, +amplify it and write the resulting Wav file in memory. +It also shows how to access the corresponding data and display it using numpy. + +""" +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. +# +# Load Ansys & other libraries. + +import matplotlib.pyplot as plt + +from ansys.dpf.sound.examples_helpers import get_absolute_path_for_flute_wav +from ansys.dpf.sound.server_helpers import connect_to_or_start_server +from ansys.dpf.sound.signal_utilities import ApplyGain, LoadWav, Resample, WriteWav + +# Connect to remote or start a local server +connect_to_or_start_server() + +# %% +# Load a Wav Signal +# ~~~~~~~~~~~~~~~~~ +# Load a wav signal using LoadWav class, it will be returned as a +# `DPF Field Container `_ # noqa: E501 + +# Returning the input data of the example file +path_flute_wav = get_absolute_path_for_flute_wav() + +# Load the wav file. +wav_loader = LoadWav(path_flute_wav) +wav_loader.process() +fc_signal_original = wav_loader.get_output() + +# %% +# Resample the signal +# ~~~~~~~~~~~~~~~~~~~ +# Change the sampling frequency of the loaded signal. +resampler = Resample(fc_signal_original, new_sampling_frequency=20000.0) +resampler.process() +fc_signal_resampled = resampler.get_output() + +t1 = fc_signal_original[0].time_freq_support.time_frequencies.data +sf1 = 1.0 / (t1[1] - t1[0]) +print( + f"The sampling frequency of the original signal is {sf1:.1f} Hz" +) # ":.1f" is to only get 1 decimal + +# %% +# Apply a gain to the signal +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Change the gain of the resampled signal. +gain = 10.0 +gain_applier = ApplyGain(fc_signal_resampled, gain=gain, gain_in_db=True) +gain_applier.process() +fc_signal_modified = gain_applier.get_output() + +t2 = fc_signal_modified[0].time_freq_support.time_frequencies.data +sf2 = 1.0 / (t2[1] - t2[0]) +print( + f"The new sampling frequency of the signal is {sf2:.1f} Hz" +) # ":.1f" is to only get 1 decimal + +# %% +# Plotting signals +# ~~~~~~~~~~~~~~~~ +# Plot the original and the modified signals. +data_original = wav_loader.get_output_as_nparray() +data_modified = gain_applier.get_output_as_nparray() + +fig, axs = plt.subplots(2) +fig.suptitle("Signals") + +axs[0].plot(t1, data_original, color="g", label=f"original signal, sf={sf1:.1f} Hz") +axs[0].set_ylabel("Pa") +axs[0].legend(loc="upper right") +axs[0].set_ylim([-3, 3]) + +axs[1].plot( + t2, data_modified, color="r", label=f"modified signal, sf={sf2:.1f} Hz, gain={gain} dBSPL" +) +axs[1].set_xlabel("s") +axs[1].set_ylabel("Pa") +axs[1].legend(loc="upper right") +axs[1].set_ylim([-3, 3]) + +plt.show() + +# %% +# Write the signals in memory +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Write the modified signal in memory using WriteWav class +output_path = path_flute_wav[:-4] + "_modified.wav" # "[-4]" is to remove the ".wav" +wav_writer = WriteWav(path_to_write=output_path, signal=fc_signal_modified, bit_depth="int16") +wav_writer.process() +print("End of script reached") diff --git a/examples/002_compute_stft.py b/examples/002_compute_stft.py new file mode 100644 index 000000000..9e76a8117 --- /dev/null +++ b/examples/002_compute_stft.py @@ -0,0 +1,84 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. + +""" +.. _compute_stft_example: + +Compute the STFT and ISTFT +-------------------------- + +This example shows how to compute a STFT (Short-Time Fourier Transform) of a signal. + +It also shows how to compute the inverse-STFT of a signal. + +""" +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. +# +# Load Ansys libraries. + +from ansys.dpf.sound.examples_helpers import get_absolute_path_for_flute_wav +from ansys.dpf.sound.server_helpers import connect_to_or_start_server +from ansys.dpf.sound.signal_utilities import LoadWav +from ansys.dpf.sound.spectrogram_processing import Istft, Stft + +# Connect to remote or start a local server +connect_to_or_start_server() + +# %% +# Load a wav signal using LoadWav class + +# Returning the input data of the example file +path_flute_wav = get_absolute_path_for_flute_wav() + +# Loading the wav file +wav_loader = LoadWav(path_flute_wav) +wav_loader.process() +fc_signal = wav_loader.get_output() + +# Plotting the input signal +wav_loader.plot() + +# %% +# Instantiate an STFT class using the previously loaded signal as an input +# Using an FFT Size of 1024 + +stft = Stft(fc_signal, fft_size=1024) + +# Processing the STFT +stft.process() + +# Plotting the output +stft.plot() + +# %% +# Modify STFT parameters using the setters of the Stft class. + +stft.fft_size = 4096 +stft.window_overlap = 0.1 +stft.window_type = "BARTLETT" + +# Re-processing the STFT with newly set parameters +stft.process() + +# Plotting the modified output +stft.plot() + +# %% +# Re-obtain time-domain signal by using the Istft class. +# The input of the Istft class is the output STFT previously computed. + +fc_stft = stft.get_output() + +# Instantiating the class +istft = Istft(fc_stft) + +# Processing the ISTFT +istft.process() + +# %% +# Finally plot the output which is the original signal. + +istft.plot() diff --git a/examples/007_calculate_psychoacoustic_indicators.py b/examples/007_calculate_psychoacoustic_indicators.py new file mode 100644 index 000000000..551f00dc0 --- /dev/null +++ b/examples/007_calculate_psychoacoustic_indicators.py @@ -0,0 +1,145 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. + +""" +.. _calculate_psychoacoustic_indicators: + +Calculate psychoacoustic indicators +----------------------------------- + +This example shows how to calculate psychoacoustic indicators. +It also shows how to plot specific loudness. + +""" +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. +# +# Load Ansys libraries. + +import os + +from ansys.dpf.sound.examples_helpers import ( + get_absolute_path_for_flute2_wav, + get_absolute_path_for_flute_wav, +) +from ansys.dpf.sound.psychoacoustics.fluctuation_strength import FluctuationStrength +from ansys.dpf.sound.psychoacoustics.loudness_iso_532_1_stationary import ( + LoudnessISO532_1_Stationary, +) +from ansys.dpf.sound.psychoacoustics.roughness import Roughness +from ansys.dpf.sound.psychoacoustics.sharpness import Sharpness +from ansys.dpf.sound.server_helpers import connect_to_or_start_server +from ansys.dpf.sound.signal_utilities import LoadWav + +# Connect to remote or start a local server. +server = connect_to_or_start_server() + +# %% +# Calculate ISO 532-1 loudness for a stationary sound +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Load a wav signal using LoadWav class, it will be returned as a +# `DPF Field Container `_ # noqa: E501 +path_flute_wav = get_absolute_path_for_flute_wav() +wav_loader = LoadWav(path_flute_wav) +wav_loader.process() +fc_signal = wav_loader.get_output() + +# Create a Loudness object, set its signal, and compute loudness. +loudness = LoudnessISO532_1_Stationary(signal=fc_signal) +loudness.process() + +# %% +# Get value in sone or in phon. +loudness_sone = loudness.get_loudness_sone() +loudness_level_phon = loudness.get_loudness_level_phon() +file_name = os.path.basename(path_flute_wav) +print( + f"\nThe loudness of sound file {file_name} " + f"is{loudness_sone: .1f} sones " + f"or{loudness_level_phon: .1f} phons.\n" +) + +# %% +# Plot the specific loudness. +loudness.plot() + +# %% +# Calculate ISO 532-1 loudness for several signals at once +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Load another wav file. +path_flute2_wav = get_absolute_path_for_flute2_wav() +wav_loader = LoadWav(path_flute2_wav) +wav_loader.process() + +# Store the second signal with the first one. +fc_two_signals = fc_signal +fc_two_signals.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + +# %% +# Calculate loudness for both signals at once. +loudness = LoudnessISO532_1_Stationary(signal=fc_two_signals) +loudness.process() + +# %% +# Get values in sone or in phon. +loudness_sone2 = loudness.get_loudness_sone(1) +loudness_level_phon2 = loudness.get_loudness_level_phon(1) +file_name2 = os.path.basename(path_flute2_wav) +print( + f"The loudness of sound file {file_name} " + f"is{loudness_sone: .1f} sones " + f"or{loudness_level_phon: .1f} phons,\n" # noqa: E231 + f"whereas the loudness of sound file {file_name2} " + f"is{loudness_sone2: .1f} sones " + f"or{loudness_level_phon2: .1f} phons.\n" +) + +# %% +# Plot specific loudness for both signals into a single figure. Note how the first sound has a +# higher specific loudness than the second. +loudness.plot() + +# %% +# Calculate sharpness, roughness, and fluctuation strength +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Now let's calculate sharpness, roughness, and fluctuation strength for these two sounds. + +# %% +# Calculate sharpness. +sharpness = Sharpness(signal=fc_two_signals) +sharpness.process() +sharpness_values = (sharpness.get_sharpness(0), sharpness.get_sharpness(1)) + +# %% +# Calculate roughness. +roughness = Roughness(signal=fc_two_signals) +roughness.process() +roughness_values = (roughness.get_roughness(0), roughness.get_roughness(1)) + +# %% +# Calculate fluctuation strength. +fluctuation_strength = FluctuationStrength(signal=fc_two_signals) +fluctuation_strength.process() +fluctuation_strength_values = ( + fluctuation_strength.get_fluctuation_strength(0), + fluctuation_strength.get_fluctuation_strength(1), +) + +# Print out the results. +print( + f"The sharpness of sound file {file_name} " + f"is{sharpness_values[0]: .2f} acum, " + f"its roughness is{roughness_values[0]: .2f} asper, " + f"and its fluctuation strength is{fluctuation_strength_values[0]: .2f} vacil.\n" + f"For sound file {file_name2}, these indicators' values are, respectively, " + f"{sharpness_values[1]: .2f} acum, " + f"{roughness_values[1]: .2f} asper, " + f"and{fluctuation_strength_values[1]: .2f} vacil.\n" +) + +# %% +# End of script. +print("End of script reached") diff --git a/examples/README.rst b/examples/README.rst new file mode 100644 index 000000000..8b8346e44 --- /dev/null +++ b/examples/README.rst @@ -0,0 +1,6 @@ +.. _gallery: + +======== +Examples +======== +These examples demonstrate the behavior and usage of PyDPF Sound. \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..444286144 --- /dev/null +++ b/index.html @@ -0,0 +1,672 @@ + + + + + + + + + + + PyAnsys Sound — PyAnsys Sound + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+
+
+ + +
+
+ + +
+ +
+
+ +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ +
+ +
+ + +
+
+ + + + + +
+ +
+

PyAnsys Sound#

+
+
+

PyAnsys Sound lets you use the main features of Ansys Sound to perform +the postprocessing and analysis of sounds and vibrations in Python. This library is based on +PyDPF-Core and the DPF Sound plugin. It is a Python wrapper that implements classes on top +of DPF Sound operators.

+

These documentation sections provide information on installing and using PyAnsys Sound:

+
+
+
+
+
+
+Getting started
+

Learn how to install PyAnsys Sound and its prerequisites in user mode.

+
+Getting started
+
+
+
+
+
+User guide
+

Learn how to start a DPF server, load a signal from a WAV files, and perform operations +on the signal.

+
+User guide
+
+
+
+
+
+API reference
+

Understand how to use Python to interact programmatically with PyAnsys Sound.

+
+API reference
+
+
+
+
+
+Examples
+

Explore examples that show how to use PyAnsys Sound for various workflows.

+
+Examples
+
+
+
+
+
+Contribute
+

Learn how to contribute to the PyAnsys Sound codebase or documentation.

+
+Contribute
+
+
+
+
+

Key features#

+

The following API reference pages describe some key features of PyAnsys Sound:

+
    +
  • Signal utilities: Tools to read and write WAV files in the format used by Ansys Sound Analysis +and Specification (SAS) and tools to perform basic editing of audio signals.

  • +
  • Spectrogram processing: Time-frequency and time-representation conversion tools and time-frequency editing tools.

  • +
  • Psychoacoustics: Psychoacoustic indicators computation to measure perceived sound quality.

  • +
  • Xtract: Separation of a signal into several components, such as tonal, transient, and noise parts.

  • +
+

Not all features of Ansys SAS are available in PyAnsys Sound. Features are regularly +added in new versions.

+
+
+ + +
+ + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..dd2e17af2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,77 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +# Check https://flit.readthedocs.io/en/latest/pyproject_toml.html for all available sections +name = "ansys-dpf-sound" +version = "0.1.dev0" +description = "A Python wrapper for Ansys DPF Sound." +readme = "README.rst" +requires-python = ">=3.9,<4" +license = {file = "LICENSE"} +authors = [ + {name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}, +] +maintainers = [ + {name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}, +] +classifiers=[ + "Development Status :: 4 - Beta", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "License :: OSI Approved :: MIT License", + "Operating System :: Windows", +] +dependencies = [ + "ansys-dpf-core>=0.9.0,<1", + "matplotlib>=3.8.2,<4", +] + +[project.optional-dependencies] +tests = [ + "ansys-dpf-core==0.11.0", + "matplotlib==3.8.4", + "pytest==7.4.3", + "pytest-cov==4.1.0", + "pytest-dependency==0.6.0" +] +doc = [ + "Sphinx==7.2.6", + "ansys-sphinx-theme==0.12.5", + "numpydoc==1.6.0", + "sphinx-autodoc-typehints==1.25.2", + "sphinx-copybutton==0.5.2", + "sphinx-gallery==0.15.0", + "sphinx-design==0.5.0", + "vtk==9.3.0", + "trame==3.5.0", + "trame_vtk==2.6.3", + "pyvista==0.43.1", + "pypandoc==1.12.0", + "jupyter_sphinx==0.4.0", + "sphinx-autobuild==2021.3.14", +] + +[tool.black] +line-length = 100 + +[tool.isort] +profile = "black" +force_sort_within_sections = true +line_length = 100 +default_section = "THIRDPARTY" +src_paths = ["doc", "src", "tests"] + +[tool.coverage.run] +source = ["ansys.dpf.sound"] + +[tool.coverage.report] +show_missing = true + +[tool.pytest.ini_options] +minversion = "7.1" +addopts = "-ra --cov=ansys.dpf.sound --cov-report html:.cov/html --cov-report xml:.cov/xml --cov-report term -vv" +testpaths = ["tests"] diff --git a/robots.txt b/robots.txt new file mode 100644 index 000000000..069ac104e --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * + + +Sitemap: https://sound.docs.pyansys.com/sitemap.xml diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 000000000..101cbb1d6 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,1755 @@ + + + + https://sound.docs.pyansys.com + 2024-08-08T09:10:37+00:00 + daily + 1.0 + + + https://sound.docs.pyansys.com/version/stable/genindex.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/contribute.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/search.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/helpers.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/index.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/psychoacoustics.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_specific_fluctuation_strength.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_phase_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.duration_zeros.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_frequencies.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_time_scale.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.fft_size.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_non_transient_signals.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.local_emergence.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_reference_curve.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_non_tonal_signals.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.minimum_duration.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_indexes.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.psd.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_noise_samples.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_levels.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L5_phon.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_overlap.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_parameters.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_level_phon_vs_time.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.output_remainder_signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_frequencies.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_low_frequencies.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_overlap.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_PR_values.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.bit_depth.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_denoised_signals.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_parameters.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.path_to_wav.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_frequencies.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.output_transient_signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.frequency_list.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_sone.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.frequency_list.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_parameters_as_generic_data_container.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.unit.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_single_tone_info.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_sone_vs_time.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_indexes.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_frequencies.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.maximum_slope.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_nb_tones.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_max_TNR_value.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.fft_size.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_reference_curve.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_levels.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_sharpness.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_specific_loudness.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_frequencies.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.noise_psd.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.convert_fields_container_to_np_array.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_single_tone_info.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.stft.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output_as_nparray.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_transient.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.output_noise_signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_type.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_signal.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_nb_tones.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.signals.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.process.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.plot.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_tonal_signals.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_automatic_estimation.html + 2024-08-08T09:10:37+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N10_sone.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.lower_threshold.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.plot.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_magnitude_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.plot.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_high_frequencies.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_tonal.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_level_phon.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L10_phon.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.signal.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain_in_db.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_max_PR_value.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.server_helpers._validate_dpf_sound_connection.validate_dpf_sound_connection.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.data.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.new_sampling_frequency.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.orders.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_TNR_values.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.rpm_profile.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.plot.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_low_frequencies.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.plot.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.input_signal.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_fluctuation_strength.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.signal.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.Resample.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.fft_size.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_parameters.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.plot.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.plot.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_transient_signals.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.end_time.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_white_noise_level.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N5_sone.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_denoiser.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.psd.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.server_helpers._connect_to_or_start_server.connect_to_or_start_server.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.plot.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_parameters_as_generic_data_container.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.upper_threshold.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.Xtract.output_tonal_signal.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.width_selection.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_parameters_as_generic_data_container.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_signal.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.signal.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_type.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonal.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_noise_signals.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.path_to_write.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_high_frequencies.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.intertonal_gap.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.sampling_frequency.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_specific_roughness.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.convert_fields_container_to_np_array.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_indexes.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.start_time.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.process.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output_as_nparray.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.regularity.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_roughness.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/signal_utilities.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/spectrogram_processing.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/api/xtract.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/index.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/py-modindex.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/_static/webpack-macros.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/sg_execution_times.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/user_guide.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/index.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/005_xtract_feature.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/007_calculate_psychoacoustic_indicators.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/002_load_resample_amplify_write_wav_files.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/001_initialize_server_and_deal_with_license.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/004_isolate_orders.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/003_compute_stft.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/sg_execution_times.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/examples/gallery_examples/006_calculate_PR_and_TNR.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + + https://sound.docs.pyansys.com/version/stable/getting_started.html + 2024-08-08T09:10:38+00:00 + weekly + 0.5 + + diff --git a/src/ansys/dpf/sound/__init__.py b/src/ansys/dpf/sound/__init__.py new file mode 100644 index 000000000..1f226d580 --- /dev/null +++ b/src/ansys/dpf/sound/__init__.py @@ -0,0 +1,17 @@ +""" +pydpf-sound. + +Library +""" + +# Version +# ------------------------------------------------------------------------------ + +import importlib.metadata as importlib_metadata + +__version__ = importlib_metadata.version(__name__.replace(".", "-")) +"""PyDPF Sound version.""" + +from . import examples_helpers, server_helpers, signal_utilities + +__all__ = ("examples_helpers", "server_helpers", "signal_utilities") diff --git a/src/ansys/dpf/sound/examples_helpers/__init__.py b/src/ansys/dpf/sound/examples_helpers/__init__.py new file mode 100644 index 000000000..3c1f5cac4 --- /dev/null +++ b/src/ansys/dpf/sound/examples_helpers/__init__.py @@ -0,0 +1,26 @@ +"""Utilities for managing the DPF Sound example files. + +Helper functions for managing the DPF Sound example files. +""" + +from ._get_example_files import ( + get_absolute_path_for_fluctuating_noise_wav, + get_absolute_path_for_fluctuating_tone_wav, + get_absolute_path_for_flute2_wav, + get_absolute_path_for_flute_wav, + get_absolute_path_for_rough_noise_wav, + get_absolute_path_for_rough_tone_wav, + get_absolute_path_for_sharp_noise_wav, + get_absolute_path_for_sharper_noise_wav, +) + +__all__ = ( + "get_absolute_path_for_flute_wav", + "get_absolute_path_for_flute2_wav", + "get_absolute_path_for_sharp_noise_wav", + "get_absolute_path_for_sharper_noise_wav", + "get_absolute_path_for_rough_noise_wav", + "get_absolute_path_for_rough_tone_wav", + "get_absolute_path_for_fluctuating_noise_wav", + "get_absolute_path_for_fluctuating_tone_wav", +) diff --git a/src/ansys/dpf/sound/examples_helpers/_get_example_files.py b/src/ansys/dpf/sound/examples_helpers/_get_example_files.py new file mode 100644 index 000000000..210d02b3a --- /dev/null +++ b/src/ansys/dpf/sound/examples_helpers/_get_example_files.py @@ -0,0 +1,116 @@ +"""Helpers to get examples files for PyDPF Sound.""" +import os +import pathlib + + +def get_absolute_path_for_flute_wav() -> str: + r"""Get the absolute path for the file flute.wav. + + Returns + ------- + str + Absolute path to flute.wav . + """ + return _get_absolute_path("flute.wav") + + +def get_absolute_path_for_flute2_wav() -> str: + r"""Get the absolute path for the file flute2.wav. + + Returns + ------- + str + Absolute path to flute2.wav . + """ + return _get_absolute_path("flute2.wav") + + +def get_absolute_path_for_sharp_noise_wav() -> str: + r"""Get the absolute path for the file sharp_noise.wav. + + Returns + ------- + str + Absolute path to sharp_noise.wav . + """ + return _get_absolute_path("sharp_noise.wav") + + +def get_absolute_path_for_sharper_noise_wav() -> str: + r"""Get the absolute path for the file sharper_noise.wav. + + Returns + ------- + str + Absolute path to sharper_noise.wav . + """ + return _get_absolute_path("sharper_noise.wav") + + +def get_absolute_path_for_rough_noise_wav() -> str: + r"""Get the absolute path for the file rough_noise.wav. + + Returns + ------- + str + Absolute path to rough_noise.wav . + """ + return _get_absolute_path("rough_noise.wav") + + +def get_absolute_path_for_rough_tone_wav() -> str: + r"""Get the absolute path for the file rough_tone.wav. + + Returns + ------- + str + Absolute path to rough_tone.wav . + """ + return _get_absolute_path("rough_tone.wav") + + +def get_absolute_path_for_fluctuating_noise_wav() -> str: + r"""Get the absolute path for the file fluctuating_noise.wav. + + Returns + ------- + str + Absolute path to fluctuating_noise.wav . + """ + return _get_absolute_path("fluctuating_noise.wav") + + +def get_absolute_path_for_fluctuating_tone_wav() -> str: + r"""Get the absolute path for the file fluctuating_tone.wav. + + Returns + ------- + str + Absolute path to fluctuating_tone.wav . + """ + return _get_absolute_path("fluctuating_tone.wav") + + +def _get_absolute_path(filename: str) -> str: + r"""Get the absolute path for the file specified in filename. + + Parameters + ---------- + filename: str + Wav file name for which to get the path. + + Returns + ------- + str + Absolute path to specified wav file name. + """ + # In case of CI/CD pipelines + port_in_env = os.environ.get("ANSRV_DPF_SOUND_PORT") + if port_in_env is not None: + return "C:\\data\\" + filename + + # Obtaining flute.wav path based on the current path + for parent in pathlib.Path(__file__).parents: # pragma: no cover + if (parent / "tests/data/" / filename).exists(): + p = parent / "tests/data/" / filename + return p.as_posix() diff --git a/src/ansys/dpf/sound/psychoacoustics/__init__.py b/src/ansys/dpf/sound/psychoacoustics/__init__.py new file mode 100644 index 000000000..8990ab638 --- /dev/null +++ b/src/ansys/dpf/sound/psychoacoustics/__init__.py @@ -0,0 +1,20 @@ +"""Psychoacoustics functions. + +Helper functions related to psychoacoustics indicators computation. +""" + +from ._psychoacoustics_parent import PsychoacousticsParent +from .fluctuation_strength import FluctuationStrength +from .loudness_iso_532_1_stationary import LoudnessISO532_1_Stationary +from .loudness_iso_532_1_time_varying import LoudnessISO532_1_TimeVarying +from .roughness import Roughness +from .sharpness import Sharpness + +__all__ = ( + "PsychoacousticsParent", + "LoudnessISO532_1_Stationary", + "LoudnessISO532_1_TimeVarying", + "Sharpness", + "Roughness", + "FluctuationStrength", +) diff --git a/src/ansys/dpf/sound/psychoacoustics/_psychoacoustics_parent.py b/src/ansys/dpf/sound/psychoacoustics/_psychoacoustics_parent.py new file mode 100644 index 000000000..3f4bbce6d --- /dev/null +++ b/src/ansys/dpf/sound/psychoacoustics/_psychoacoustics_parent.py @@ -0,0 +1,51 @@ +"""Psychoacoustics functions.""" +from numpy import typing as npt + +from ..pydpf_sound import PyDpfSound, PyDpfSoundException + + +class PsychoacousticsParent(PyDpfSound): + """ + Abstract mother class for psychoacoustics calculations. + + This is the mother class of all pychoacoustics indicators classes, should not be used as is. + """ + + def __init__(self): + """Init. + + Init the class. + """ + super().__init__() + + def _convert_bark_to_hertz(self, bark_band_indexes: npt.ArrayLike) -> npt.ArrayLike: + """Convert Bark band indexes into frequencies. + + Converts input Bark band indexes (in Bark) into corresponding frequencies (in Hz), + according to: Traunmüller, Hartmut. "Analytical Expressions for the Tonotopic Sensory + Scale." Journal of the Acoustical Society of America. Vol. 88, Issue 1, 1990, pp. 97–100. + + Parameters + ---------- + bark_band_indexes: numpy array + Array of Bark band indexes to convert, in Bark. + + Returns + ------- + numpy array + Array of corresponding frequencies, in Hz. + """ + for ibark in range(len(bark_band_indexes)): + if not (0 <= bark_band_indexes[ibark] <= 24 + 1e-6): + # A slight margin (1e-6) is used for the upper limit, because the last index from + # the DPF operator is precisely 24.00000036. + raise PyDpfSoundException( + "Specified Bark band indexes must be between 0.0 and 24.0 Bark." + ) + + if bark_band_indexes[ibark] < 2: + bark_band_indexes[ibark] = (bark_band_indexes[ibark] - 0.3) / 0.85 + elif bark_band_indexes[ibark] > 20.1: + bark_band_indexes[ibark] = (bark_band_indexes[ibark] + 4.422) / 1.22 + + return 1920 * (bark_band_indexes + 0.53) / (26.28 - bark_band_indexes) diff --git a/src/ansys/dpf/sound/psychoacoustics/fluctuation_strength.py b/src/ansys/dpf/sound/psychoacoustics/fluctuation_strength.py new file mode 100644 index 000000000..2e7d7adad --- /dev/null +++ b/src/ansys/dpf/sound/psychoacoustics/fluctuation_strength.py @@ -0,0 +1,289 @@ +"""Compute fluctuation strength.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import matplotlib.pyplot as plt +import numpy as np +from numpy import typing as npt + +from . import PsychoacousticsParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + +TOTAL_FS_ID = "total" +SPECIFIC_FS_ID = "specific" + + +class FluctuationStrength(PsychoacousticsParent): + """Fluctuation strength for stationary sounds. + + This class computes the fluctuation strength of a signal, according to Sontacchi's master + thesis work: "Entwicklung eines Modulkonzeptes fur die psychoakustische Gerauschanalyse under + MATLAB". Master thesis, Technischen Universitat Graz, pp. 1-112 (1998). + """ + + def __init__(self, signal: Field | FieldsContainer = None): + """Create a FluctuationStrength object. + + Parameters + ---------- + signal: Field | FieldsContainer + Signal in Pa on which to compute fluctuation strength, as a DPF Field or Fields + Container. + """ + super().__init__() + self.signal = signal + self.__operator = Operator("compute_fluctuation_strength") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal. + + Parameters + ------- + signal: FieldsContainer | Field + Signal in Pa on which to compute fluctuation strength, as a DPF Field or Fields + Container. + + """ + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal in Pa as a Field or a FieldsContainer. + """ + return self.__signal + + def process(self): + """Compute fluctuation strength. + + Calls the appropriate DPF Sound operator to compute the fluctuation strength of the signal. + """ + if self.__signal == None: + raise PyDpfSoundException( + "No signal for fluctuation strength computation. Use FluctuationStrength.signal." + ) + + self.__operator.connect(0, self.signal) + + # Runs the operator + self.__operator.run() + + # Stores outputs in the tuple variable + if type(self.signal) == FieldsContainer: + self._output = ( + self.__operator.get_output(0, "fields_container"), + self.__operator.get_output(1, "fields_container"), + ) + elif type(self.signal) == Field: + self._output = ( + self.__operator.get_output(0, "field"), + self.__operator.get_output(1, "field"), + ) + + def get_output(self) -> tuple[FieldsContainer] | tuple[Field]: + """Return fluctuation strength data in a tuple of field or fields container. + + Returns + ------- + tuple(FieldsContainer) | tuple(Field) + First element is the fluctuation strength in vacil. + Second element is the specific fluctuation strength in vacil/Bark. + """ + if self._output == None: + warnings.warn( + PyDpfSoundWarning( + "Output has not been processed yet, use FluctuationStrength.process()." + ) + ) + + return self._output + + def get_output_as_nparray(self) -> tuple[npt.ArrayLike]: + """Return fluctuation strength data as a tuple of numpy array. + + Returns + ------- + tuple[numpy.ndarray] + First element is the fluctuation strength in vacil. + Second element is the specific fluctuation strength in vacil/Bark. + """ + output = self.get_output() + + if output == None: + return None + + if type(output[0]) == Field: + return (np.array(output[0].data), np.array(output[1].data)) + + return ( + self.convert_fields_container_to_np_array(output[0]), + self.convert_fields_container_to_np_array(output[1]), + ) + + def get_fluctuation_strength(self, channel_index: int = 0) -> np.float64: + """Return fluctuation strength in vacil. + + Returns the fluctuation strength in vacil as a float, for the specified channel index. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the specified output. + + Returns + ------- + numpy.float64 + Fluctuation strength value in vacil. + """ + return self._get_output_parameter(channel_index, TOTAL_FS_ID) + + def get_specific_fluctuation_strength(self, channel_index: int = 0) -> npt.ArrayLike: + """Return specific fluctuation strength. + + Returns the specific fluctuation strength in vacil/Bark, for the specified channel index. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the specified output. + + Returns + ------- + numpy.ndarray + Specific fluctuation strength array in vacil/Bark. + """ + return self._get_output_parameter(channel_index, SPECIFIC_FS_ID) + + def get_bark_band_indexes(self) -> npt.ArrayLike: + """Return Bark band indexes. + + Returns the Bark band indexes used for fluctuation strength calculation as a numpy array. + + Returns + ------- + numpy.ndarray + Array of Bark band idexes. + """ + output = self.get_output() + + if output == None: + return None + + specific_fluctuation_strength = output[1] + + if type(specific_fluctuation_strength) == Field: + return np.copy(specific_fluctuation_strength.time_freq_support.time_frequencies.data) + else: + return np.copy(specific_fluctuation_strength[0].time_freq_support.time_frequencies.data) + + def get_bark_band_frequencies(self) -> npt.ArrayLike: + """Return Bark band frequencies. + + Return the frequencies corresponding to Bark band indexes as a numpy array. + + Reference: + Traunmüller, Hartmut. "Analytical Expressions for the Tonotopic Sensory Scale." Journal of + the Acoustical Society of America. Vol. 88, Issue 1, 1990, pp. 97–100. + + Returns + ------- + numpy.ndarray + Array of Bark band frequencies. + """ + return self._convert_bark_to_hertz(self.get_bark_band_indexes()) + + def plot(self): + """Plot specific fluctuation strength. + + Creates a figure window where the specific fluctuation strength in vacil/Bark as a function + of Bark band index is displayed. + """ + if self._output == None: + raise PyDpfSoundException( + "Output has not been processed yet, use FluctuationStrength.process()." + ) + + bark_band_indexes = self.get_bark_band_indexes() + specific_fluctuation_strength_as_nparray = self.get_output_as_nparray()[1] + + if type(self._output[1]) == Field: + num_channels = 1 + plt.plot(bark_band_indexes, specific_fluctuation_strength_as_nparray) + else: + num_channels = len(self._output[1]) + if num_channels == 1: + plt.plot(bark_band_indexes, specific_fluctuation_strength_as_nparray) + else: + for ichannel in range(num_channels): + plt.plot( + bark_band_indexes, + specific_fluctuation_strength_as_nparray[ichannel], + label="Channel {}".format(ichannel), + ) + + plt.title("Specific fluctuation strength") + plt.xlabel("Bark band index") + plt.ylabel("Fluctuation strength (vacil/Bark)") + plt.grid(True) + if num_channels > 1: + plt.legend() + plt.show() + + def _get_output_parameter( + self, channel_index: int, output_id: str + ) -> np.float64 | npt.ArrayLike: + """Return individual fluctuation strength result. + + Returns the fluctuation strength as a float, or the specific fluctuation strength as a + numpy array, according to specified output_id, and for the specified channel. + + Parameters + ---------- + channel_index: int + Index of the signal channel for which to return the specified output. + output_id: str + Identifier of the specific output parameter that should be returned: + - "total" for overall fluctuation strength value in vacil. + - "specific" for specific fluctuation strength array in vacil/Bark. + + Returns + ------- + numpy.float64 | numpy.ndarray + Fluctuation strength (float) in vacil or specific fluctuation strength (numpy array) + in vacil/Bark. + """ + fluctuation_strength_data = self.get_output_as_nparray() + if fluctuation_strength_data == None: + return None + + # Get last channel index. + channel_max = len(fluctuation_strength_data[0]) - 1 + + # Check that specified channel index exists. + if channel_index > channel_max: + raise PyDpfSoundException(f"Specified channel index ({channel_index}) does not exist.") + + # Return output parameter (fluctuation strength or specific fluctuation strength) for the + # specified channel. + if output_id == SPECIFIC_FS_ID: + if channel_max > 0: + return fluctuation_strength_data[1][channel_index] + else: + return fluctuation_strength_data[1] + elif output_id == TOTAL_FS_ID: + if channel_max > 0: + return fluctuation_strength_data[0][channel_index][0] + else: + return fluctuation_strength_data[0][0] + else: + raise PyDpfSoundException("Invalid identifier of output parameter.") diff --git a/src/ansys/dpf/sound/psychoacoustics/loudness_iso_532_1_stationary.py b/src/ansys/dpf/sound/psychoacoustics/loudness_iso_532_1_stationary.py new file mode 100644 index 000000000..0a33b9083 --- /dev/null +++ b/src/ansys/dpf/sound/psychoacoustics/loudness_iso_532_1_stationary.py @@ -0,0 +1,315 @@ +"""Compute ISO 532-1 loudness for stationary sounds.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import matplotlib.pyplot as plt +import numpy as np +from numpy import typing as npt + +from . import PsychoacousticsParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + +LOUDNESS_SONE_ID = "sone" +LOUDNESS_LEVEL_PHON_ID = "phon" +SPECIFIC_LOUDNESS_ID = "specific" + + +class LoudnessISO532_1_Stationary(PsychoacousticsParent): + """ISO 532-1 loudness for stationary sounds. + + This class computes the loudness of a signal following standard ISO 532-1 for stationary + sounds. + """ + + def __init__(self, signal: Field | FieldsContainer = None): + """Create a LoudnessISO532_1_Stationary object. + + Parameters + ---------- + signal: Field | FieldsContainer + Signal in Pa on which to compute loudness, as a DPF Field or Fields Container. + """ + super().__init__() + self.signal = signal + self.__operator = Operator("compute_loudness_iso532_1") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal. + + Parameters + ------- + signal: FieldsContainer | Field + Signal in Pa on which to compute loudness, as a DPF Field or Fields Container. + + """ + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal in Pa as a Field or a FieldsContainer. + """ + return self.__signal + + def process(self): + """Compute loudness. + + Calls the appropriate DPF Sound operator to compute the loudness of the signal. + """ + if self.__signal == None: + raise PyDpfSoundException( + "No signal for loudness computation. Use LoudnessISO532_1_Stationary.signal." + ) + + self.__operator.connect(0, self.signal) + + # Runs the operator + self.__operator.run() + + # Stores outputs in the tuple variable + if type(self.signal) == FieldsContainer: + self._output = ( + self.__operator.get_output(0, "fields_container"), + self.__operator.get_output(1, "fields_container"), + self.__operator.get_output(2, "fields_container"), + ) + elif type(self.signal) == Field: + self._output = ( + self.__operator.get_output(0, "field"), + self.__operator.get_output(1, "field"), + self.__operator.get_output(2, "field"), + ) + + def get_output(self) -> tuple[FieldsContainer] | tuple[Field]: + """Return loudness data in a tuple of field or fields container. + + Returns + ------- + tuple(FieldsContainer) | tuple(Field) + First element is the loudness in sone. + Second element is the loudness level in phon. + Third element is the specific loudness in sone/Bark. + """ + if self._output == None: + warnings.warn( + PyDpfSoundWarning( + "Output has not been processed yet, use LoudnessISO532_1_Stationary.process()." + ) + ) + + return self._output + + def get_output_as_nparray(self) -> tuple[npt.ArrayLike]: + """Return loudness data as a tuple of numpy array. + + Returns + ------- + tuple[numpy.ndarray] + First element is the loudness in sone. + Second element is the loudness level in phon. + Third element is the specific loudness in sone/Bark. + """ + output = self.get_output() + + if output == None: + return None + + if type(output[0]) == Field: + return (np.array(output[0].data), np.array(output[1].data), np.array(output[2].data)) + + return ( + self.convert_fields_container_to_np_array(output[0]), + self.convert_fields_container_to_np_array(output[1]), + self.convert_fields_container_to_np_array(output[2]), + ) + + def get_loudness_sone(self, channel_index: int = 0) -> np.float64: + """Return loudness in sone. + + Returns the loudness in sone as a float, for the specified channel index. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the specified output. + + Returns + ------- + numpy.float64 + Loudness value in sone. + """ + return self._get_output_parameter(channel_index, LOUDNESS_SONE_ID) + + def get_loudness_level_phon(self, channel_index: int = 0) -> np.float64: + """Return loudness level in phon. + + Returns the loudness level in phon as a float, for the specified channel index. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the specified output. + + Returns + ------- + numpy.float64 + Loudness level value in phon. + """ + return self._get_output_parameter(channel_index, LOUDNESS_LEVEL_PHON_ID) + + def get_specific_loudness(self, channel_index: int = 0) -> npt.ArrayLike: + """Return specific loudness. + + Returns the specific loudness in sone/Bark, for the specified channel index. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the specified output. + + Returns + ------- + numpy.ndarray + Specific loudness array in sone/Bark. + """ + return self._get_output_parameter(channel_index, SPECIFIC_LOUDNESS_ID) + + def get_bark_band_indexes(self) -> npt.ArrayLike: + """Return Bark band indexes. + + Returns the Bark band indexes used for loudness calculation as a numpy array. + + Returns + ------- + numpy.ndarray + Array of Bark band idexes. + """ + output = self.get_output() + + if output == None: + return None + + specific_loudness = output[2] + + if type(specific_loudness) == Field: + return np.copy(specific_loudness.time_freq_support.time_frequencies.data) + else: + return np.copy(specific_loudness[0].time_freq_support.time_frequencies.data) + + def get_bark_band_frequencies(self) -> npt.ArrayLike: + """Return Bark band frequencies. + + Return the frequencies corresponding to Bark band indexes as a numpy array. + + Reference: + Traunmüller, Hartmut. "Analytical Expressions for the Tonotopic Sensory Scale." Journal of + the Acoustical Society of America. Vol. 88, Issue 1, 1990, pp. 97–100. + + Returns + ------- + numpy.ndarray + Array of Bark band frequencies. + """ + return self._convert_bark_to_hertz(self.get_bark_band_indexes()) + + def plot(self): + """Plot specific loudness. + + Creates a figure window where the specific loudness in sone/Bark as a function of Bark band + index is displayed. + """ + if self._output == None: + raise PyDpfSoundException( + "Output has not been processed yet, use LoudnessISO532_1_Stationary.process()." + ) + + bark_band_indexes = self.get_bark_band_indexes() + specific_loudness_as_nparray = self.get_output_as_nparray()[2] + + if type(self._output[2]) == Field: + num_channels = 1 + plt.plot(bark_band_indexes, specific_loudness_as_nparray) + else: + num_channels = len(self._output[2]) + if num_channels == 1: + plt.plot(bark_band_indexes, specific_loudness_as_nparray) + else: + for ichannel in range(num_channels): + plt.plot( + bark_band_indexes, + specific_loudness_as_nparray[ichannel], + label="Channel {}".format(ichannel), + ) + + plt.title("Specific loudness") + plt.xlabel("Bark band index") + plt.ylabel("N' (sone/Bark)") + plt.grid(True) + if num_channels > 1: + plt.legend() + plt.show() + + def _get_output_parameter( + self, channel_index: int, output_id: str + ) -> np.float64 | npt.ArrayLike: + """Return individual loudness result. + + Returns the loudness or loudness level in phon as a float, or the specific loudness as a + numpy array, according to specified output_id, and for the specified channel. + + Parameters + ---------- + channel_index: int + Index of the signal channel for which to return the specified output. + output_id: str + Identifier of the specific output parameter that should be returned: + - "sone" for overall loudness value in sone. + - "phon" for overall loudness level value in phon. + - "specific" for specific loudness array in sone/Bark. + + Returns + ------- + numpy.float64 | numpy.ndarray + Loudness or loudness level value (float, in sone or phon, respectively), or specific + loudness (numpy array, in sone/Bark). + """ + loudness_data = self.get_output_as_nparray() + if loudness_data == None: + return None + + # Get last channel index. + channel_max = len(loudness_data[0]) - 1 + + # Check that specified channel index exists. + if channel_index > channel_max: + raise PyDpfSoundException(f"Specified channel index ({channel_index}) does not exist.") + + # Return output parameter (loudness, loudness level, or specific loudness) for the specified + # channel. + if output_id == SPECIFIC_LOUDNESS_ID: + if channel_max > 0: + return loudness_data[2][channel_index] + else: + return loudness_data[2] + else: + if output_id == LOUDNESS_SONE_ID: + unit_index = 0 + elif output_id == LOUDNESS_LEVEL_PHON_ID: + unit_index = 1 + else: + raise PyDpfSoundException("Invalid identifier of output parameter.") + + if channel_max > 0: + return loudness_data[unit_index][channel_index][0] + else: + return loudness_data[unit_index][0] diff --git a/src/ansys/dpf/sound/psychoacoustics/loudness_iso_532_1_time_varying.py b/src/ansys/dpf/sound/psychoacoustics/loudness_iso_532_1_time_varying.py new file mode 100644 index 000000000..dab11a58b --- /dev/null +++ b/src/ansys/dpf/sound/psychoacoustics/loudness_iso_532_1_time_varying.py @@ -0,0 +1,422 @@ +"""Compute ISO 532-1 loudness for time-varying sounds.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import matplotlib.pyplot as plt +import numpy as np +from numpy import typing as npt + +from . import PsychoacousticsParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class LoudnessISO532_1_TimeVarying(PsychoacousticsParent): + """ISO 532-1 loudness for time-varying sounds. + + This class computes the loudness of a signal following standard ISO 532-1 for time-varying + sounds. + """ + + def __init__(self, signal: Field | FieldsContainer = None): + """Create a LoudnessISO532_1_TimeVarying object. + + Parameters + ---------- + signal: Field | FieldsContainer + Signal on which to compute time-varying ISO532-1 Loudness, as a DPF field or fields + container. + """ + super().__init__() + self.signal = signal + self.__operator = Operator("compute_loudness_iso532_1_vs_time") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal. + + Parameters + ------- + signal: FieldsContainer | Field + Signal in Pa on which to compute loudness, as a DPF field or fields container. + """ + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal as a field or a fields container + """ + return self.__signal + + def process(self): + """Compute the time-varying ISO532-1 Loudness. + + Calls the appropriate DPF Sound operator to compute the loudness of the signal. + """ + if self.__signal == None: + raise PyDpfSoundException( + "No signal for loudness vs time computation." + + " Use LoudnessISO532_1_TimeVarying.signal" + ) + + self.__operator.connect(0, self.signal) + + # Runs the operator + self.__operator.run() + + # Stores outputs in the tuple variable + if type(self.signal) == FieldsContainer: + self._output = ( + self.__operator.get_output(0, "fields_container"), + self.__operator.get_output(1, "fields_container"), + self.__operator.get_output(2, "fields_container"), + self.__operator.get_output(3, "fields_container"), + self.__operator.get_output(4, "fields_container"), + self.__operator.get_output(5, "fields_container"), + ) + elif type(self.signal) == Field: + self._output = ( + self.__operator.get_output(0, "field"), + self.__operator.get_output(1, "field"), + self.__operator.get_output(2, "field"), + self.__operator.get_output(3, "field"), + self.__operator.get_output(4, "field"), + self.__operator.get_output(5, "field"), + ) + + def get_output(self) -> tuple[FieldsContainer] | tuple[Field]: + """Return time-varying loudness data in a tuple of fields or fields. + + Returns + ------- + tuple(FieldsContainer) | tuple(Field) + 1st element is the loudness vs time in sone. + 2nd element is the N5 indicator, in sone. + 3rd element is the N10 indicator, in sone. + 4th element is the loudness vs time in phon. + 5th element is the L5 indicator, in phon. + 6th element is the L10 indicator, in phon. + """ + if self._output == None: + warnings.warn( + PyDpfSoundWarning( + "Output has not been processed yet, use LoudnessISO532_1_TimeVarying.process()." + ) + ) + + return self._output + + def get_output_as_nparray(self) -> tuple[npt.ArrayLike]: + """Return the time-varying loudness related indicators as numpy arrays. + + Returns + ------- + tuple[np.array] + 1st element is the loudness vs time in sone. + 2nd element is the N5 indicator, in sone. + 3rd element is the N10 indicator, in sone. + 4th element is the loudness level vs time in phon. + 5th element is the L5 indicator, in phon. + 6th element is the L10 indicator, in phon. + """ + output = self.get_output() + + if output == None: + return None + + if type(output[0]) == Field: + return ( + np.array(output[0].data), + np.array(output[1].data), + np.array(output[2].data), + np.array(output[3].data), + np.array(output[4].data), + np.array(output[5].data), + ) + + return ( + self.convert_fields_container_to_np_array(output[0]), + self.convert_fields_container_to_np_array(output[1]), + self.convert_fields_container_to_np_array(output[2]), + self.convert_fields_container_to_np_array(output[3]), + self.convert_fields_container_to_np_array(output[4]), + self.convert_fields_container_to_np_array(output[5]), + ) + + def get_loudness_sone_vs_time(self, channel_index: int = 0) -> npt.ArrayLike: + """Return the time-varying loudness in sone for the specified channel_index. + + Parameters + ------- + channel_index: int + Index of the signal channel (0 by default) for which to return time-varying loudness. + + Returns + ------- + numpy.ndarray + Time-varying loudness in sone. + """ + if self.get_output() == None: + return None + + # check validity of input -> will raise an exception if channel index is incorrect + self.__check_channel_index(channel_index) + + if type(self._output[0]) == Field: + return self.get_output_as_nparray()[0] + + else: + loudness_vs_time = self.get_output_as_nparray()[0] + if loudness_vs_time.ndim == 1: + # Only one field + return loudness_vs_time + else: + return loudness_vs_time[channel_index] + + def get_N5_sone(self, channel_index: int = 0) -> float: + """Return the N5 indicator. + + Returns N5, that is, the loudness value in sone that is exceeded 5% of the time, for the + specified channel. + + Parameters + ------- + channel_index: int + Index of the signal channel (0 by default) for which to return N5. + + Returns + ------- + numpy.float64 + N5 value in sone. + """ + if self.get_output() == None: + return None + + # check validity of input -> will raise an exception if channel index is incorrect + self.__check_channel_index(channel_index) + + if type(self._output[0]) == Field: + return self.get_output_as_nparray()[1] + + else: + N5 = self.get_output_as_nparray()[1] + return N5[channel_index] + + def get_N10_sone(self, channel_index: int = 0) -> float: + """Return the N10 indicator. + + Returns N10, that is, the loudness value in sone that is exceeded 10% of the time, for the + specified channel. + + Parameters + ------- + channel_index: int + Index of the signal channel (0 by default) for which to return N10. + + Returns + ------- + numpy.float64 + N10 value in sone. + """ + if self.get_output() == None: + return None + + # check validity of input -> will raise an exception if channel index is incorrect + self.__check_channel_index(channel_index) + + if type(self._output[0]) == Field: + return self.get_output_as_nparray()[2] + + else: + N10 = self.get_output_as_nparray()[2] + return N10[channel_index] + + def get_loudness_level_phon_vs_time(self, channel_index: int = 0) -> npt.ArrayLike: + """Return the time-varying loudness level in phon for the specified channel_index. + + Parameters + ------- + channel_index: int + Index of the signal channel (0 by default) for which to return time-varying loudness + level. + + Returns + ------- + numpy.ndarray + Time-varying loudness level in phon. + """ + if self.get_output() == None: + return None + + # check validity of input -> will raise an exception if channel index is incorrect + self.__check_channel_index(channel_index) + + if type(self._output[0]) == Field: + return self.get_output_as_nparray()[3] + + else: + loudness_level_vs_time = self.get_output_as_nparray()[3] + if loudness_level_vs_time.ndim == 1: + # Only one field + return loudness_level_vs_time + else: + return loudness_level_vs_time[channel_index] + + def get_L5_phon(self, channel_index: int = 0) -> float: + """Return the L5 indicator. + + Returns L5, that is, the loudness level in phon that is exceeded 5% of the time, for the + specified channel. + + Parameters + ------- + channel_index: int + Index of the signal channel (0 by default) for which to return L5. + + Returns + ------- + numpy.float64 + L5 value in phon. + """ + if self.get_output() == None: + return None + + # check validity of input -> will raise an exception if channel index is incorrect + self.__check_channel_index(channel_index) + + if type(self._output[0]) == Field: + return self.get_output_as_nparray()[4] + + else: + L5 = self.get_output_as_nparray()[4] + return L5[channel_index] + + def get_L10_phon(self, channel_index: int = 0) -> float: + """Return the L10 indicator. + + Returns L10, that is, the loudness level in phon that is exceeded 10% of the time, for the + specified channel. + + Parameters + ------- + channel_index: int + Index of the signal channel (0 by default) for which to return L10. + + Returns + ------- + numpy.float64 + L10 value in phon. + """ + if self.get_output() == None: + return None + + # check validity of input -> will raise an exception if channel index is incorrect + self.__check_channel_index(channel_index) + + if type(self._output[0]) == Field: + return self.get_output_as_nparray()[5] + + else: + L10 = self.get_output_as_nparray()[5] + return L10[channel_index] + + def get_time_scale(self) -> npt.ArrayLike: + """Return time scale. + + Returns an array of the timestamps, in second, where time-varying loudness and loudness + level are defined. + + Returns + ------- + numpy.ndarray + Timestamps in second. + """ + if self.get_output() == None: + return None + + if type(self._output[0]) == Field: + return np.copy(self._output[0].time_freq_support.time_frequencies.data) + else: + return np.copy(self._output[0][0].time_freq_support.time_frequencies.data) + + def plot(self): + """Plot the time-varying loudness in sone and loudness level in phon. + + Creates a figure window where the time-varying loudness N in sone and loudness level L_N + in phon are displayed. + """ + if self.get_output() == None: + raise PyDpfSoundException( + "Output has not been processed yet, use LoudnessISO532_1_TimeVarying.process()." + ) + + if type(self._output[0]) == Field: + num_channels = 1 + else: + num_channels = len(self._output[0]) + + time = self.get_time_scale() + + # Plot loudness in sone + f, (ax1, ax2) = plt.subplots(2, 1, sharex=True) + for i in range(num_channels): + ax1.plot(time, self.get_loudness_sone_vs_time(i), label="Channel {}".format(i)) + + ax1.set_title("Time-varying loudness") + if num_channels > 1: + ax1.legend() + ax1.set_ylabel("N (sone)") + ax1.grid(True) + + # Plot loudness in phon + + for i in range(num_channels): + ax2.plot(time, self.get_loudness_level_phon_vs_time(i), label="Channel {}".format(i)) + + ax2.set_title("Time-varying loudness level") + if num_channels > 1: + ax2.legend() + ax2.set_xlabel("Time (s)") + ax2.set_ylabel(r"$\mathregular{L_N}$ (phon)") + ax2.grid(True) + + plt.show() + + def __check_channel_index(self, channel_index: int) -> bool: + """Check whether a specified signal channel index is available or not. + + Parameters + ------- + channel_index: int + Index of the signal channel to check. + + Returns + ------- + bool + True if channel index is available, False if not. + """ + if self.get_output() == None: + return False + + if type(self._output[0]) == Field: + if channel_index != 0: + raise PyDpfSoundException( + f"Specified channel index ({channel_index}) does not exist." + ) + + else: + if channel_index < 0 or channel_index > self.get_output_as_nparray()[0].ndim - 1: + raise PyDpfSoundException( + f"Specified channel index ({channel_index}) does not exist." + ) + + return True diff --git a/src/ansys/dpf/sound/psychoacoustics/roughness.py b/src/ansys/dpf/sound/psychoacoustics/roughness.py new file mode 100644 index 000000000..707d15664 --- /dev/null +++ b/src/ansys/dpf/sound/psychoacoustics/roughness.py @@ -0,0 +1,279 @@ +"""Compute roughness.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import matplotlib.pyplot as plt +import numpy as np +from numpy import typing as npt + +from . import PsychoacousticsParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + +TOTAL_ROUGHNESS_ID = "total" +SPECIFIC_ROUGHNESS_ID = "specific" + + +class Roughness(PsychoacousticsParent): + """Roughness for stationary sounds. + + This class computes the roughness of a signal, according to Daniel and Weber, "Psychoacoustical + roughness: implementation of an optimized model." Acta Acustica united with Acustica, 83, pp. + 113-123 (1997). + """ + + def __init__(self, signal: Field | FieldsContainer = None): + """Create a Roughness object. + + Parameters + ---------- + signal: Field | FieldsContainer + Signal in Pa on which to compute roughness, as a DPF Field or Fields Container. + """ + super().__init__() + self.signal = signal + self.__operator = Operator("compute_roughness") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal. + + Parameters + ------- + signal: FieldsContainer | Field + Signal in Pa on which to compute roughness, as a DPF Field or Fields Container. + + """ + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal in Pa as a Field or a FieldsContainer. + """ + return self.__signal + + def process(self): + """Compute roughness. + + Calls the appropriate DPF Sound operator to compute the roughness of the signal. + """ + if self.__signal == None: + raise PyDpfSoundException("No signal for roughness computation. Use Roughness.signal.") + + self.__operator.connect(0, self.signal) + + # Runs the operator + self.__operator.run() + + # Stores outputs in the tuple variable + if type(self.signal) == FieldsContainer: + self._output = ( + self.__operator.get_output(0, "fields_container"), + self.__operator.get_output(1, "fields_container"), + ) + elif type(self.signal) == Field: + self._output = ( + self.__operator.get_output(0, "field"), + self.__operator.get_output(1, "field"), + ) + + def get_output(self) -> tuple[FieldsContainer] | tuple[Field]: + """Return roughness data in a tuple of field or fields container. + + Returns + ------- + tuple(FieldsContainer) | tuple(Field) + First element is the roughness in asper. + Second element is the specific roughness in asper/Bark. + """ + if self._output == None: + warnings.warn( + PyDpfSoundWarning("Output has not been processed yet, use Roughness.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> tuple[npt.ArrayLike]: + """Return roughness data as a tuple of numpy array. + + Returns + ------- + tuple[numpy.ndarray] + First element is the roughness in asper. + Second element is the specific roughness in asper/Bark. + """ + output = self.get_output() + + if output == None: + return None + + if type(output[0]) == Field: + return (np.array(output[0].data), np.array(output[1].data)) + + return ( + self.convert_fields_container_to_np_array(output[0]), + self.convert_fields_container_to_np_array(output[1]), + ) + + def get_roughness(self, channel_index: int = 0) -> np.float64: + """Return roughness in asper. + + Returns the roughness in asper as a float, for the specified channel index. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the specified output. + + Returns + ------- + numpy.float64 + Roughness value in asper. + """ + return self._get_output_parameter(channel_index, TOTAL_ROUGHNESS_ID) + + def get_specific_roughness(self, channel_index: int = 0) -> npt.ArrayLike: + """Return specific roughness. + + Returns the specific roughness in asper/Bark, for the specified channel index. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the specified output. + + Returns + ------- + numpy.ndarray + Specific roughness array in asper/Bark. + """ + return self._get_output_parameter(channel_index, SPECIFIC_ROUGHNESS_ID) + + def get_bark_band_indexes(self) -> npt.ArrayLike: + """Return Bark band indexes. + + Returns the Bark band indexes used for roughness calculation as a numpy array. + + Returns + ------- + numpy.ndarray + Array of Bark band idexes. + """ + output = self.get_output() + + if output == None: + return None + + specific_roughness = output[1] + + if type(specific_roughness) == Field: + return np.copy(specific_roughness.time_freq_support.time_frequencies.data) + else: + return np.copy(specific_roughness[0].time_freq_support.time_frequencies.data) + + def get_bark_band_frequencies(self) -> npt.ArrayLike: + """Return Bark band frequencies. + + Return the frequencies corresponding to Bark band indexes as a numpy array. + + Reference: + Traunmüller, Hartmut. "Analytical Expressions for the Tonotopic Sensory Scale." Journal of + the Acoustical Society of America. Vol. 88, Issue 1, 1990, pp. 97–100. + + Returns + ------- + numpy.ndarray + Array of Bark band frequencies. + """ + return self._convert_bark_to_hertz(self.get_bark_band_indexes()) + + def plot(self): + """Plot specific roughness. + + Creates a figure window where the specific roughness in asper/Bark as a function of Bark + band index is displayed. + """ + if self._output == None: + raise PyDpfSoundException("Output has not been processed yet, use Roughness.process().") + + bark_band_indexes = self.get_bark_band_indexes() + specific_roughness_as_nparray = self.get_output_as_nparray()[1] + + if type(self._output[1]) == Field: + num_channels = 1 + plt.plot(bark_band_indexes, specific_roughness_as_nparray) + else: + num_channels = len(self._output[1]) + if num_channels == 1: + plt.plot(bark_band_indexes, specific_roughness_as_nparray) + else: + for ichannel in range(num_channels): + plt.plot( + bark_band_indexes, + specific_roughness_as_nparray[ichannel], + label="Channel {}".format(ichannel), + ) + + plt.title("Specific roughness") + plt.xlabel("Bark band index") + plt.ylabel("Roughness (asper/Bark)") + plt.grid(True) + if num_channels > 1: + plt.legend() + plt.show() + + def _get_output_parameter( + self, channel_index: int, output_id: str + ) -> np.float64 | npt.ArrayLike: + """Return individual roughness result. + + Returns the roughness as a float, or the specific roughness as a numpy array, according to + specified output_id, and for the specified channel. + + Parameters + ---------- + channel_index: int + Index of the signal channel for which to return the specified output. + output_id: str + Identifier of the specific output parameter that should be returned: + - "total" for overall roughness value in asper. + - "specific" for specific roughness array in asper/Bark. + + Returns + ------- + numpy.float64 | numpy.ndarray + Roughness (float) in asper or specific roughness (numpy array) in asper/Bark. + """ + roughness_data = self.get_output_as_nparray() + if roughness_data == None: + return None + + # Get last channel index. + channel_max = len(roughness_data[0]) - 1 + + # Check that specified channel index exists. + if channel_index > channel_max: + raise PyDpfSoundException(f"Specified channel index ({channel_index}) does not exist.") + + # Return output parameter (roughness or specific roughness) for the specified channel. + if output_id == SPECIFIC_ROUGHNESS_ID: + if channel_max > 0: + return roughness_data[1][channel_index] + else: + return roughness_data[1] + elif output_id == TOTAL_ROUGHNESS_ID: + if channel_max > 0: + return roughness_data[0][channel_index][0] + else: + return roughness_data[0][0] + else: + raise PyDpfSoundException("Invalid identifier of output parameter.") diff --git a/src/ansys/dpf/sound/psychoacoustics/sharpness.py b/src/ansys/dpf/sound/psychoacoustics/sharpness.py new file mode 100644 index 000000000..ddc5d2553 --- /dev/null +++ b/src/ansys/dpf/sound/psychoacoustics/sharpness.py @@ -0,0 +1,141 @@ +"""Compute sharpness according to Zwicker & Fastl's model.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import numpy as np +from numpy import typing as npt + +from . import PsychoacousticsParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class Sharpness(PsychoacousticsParent): + """Sharpness. + + This class computes the sharpness of a signal according to Zwicker & Fastl's model. + """ + + def __init__(self, signal: Field | FieldsContainer = None): + """Create a Sharpness object. + + Parameters + ---------- + signal: Field | FieldsContainer + Signal in Pa on which to compute sharpness, as a DPF Field or Fields Container. + """ + super().__init__() + self.signal = signal + self.__operator = Operator("compute_sharpness") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal. + + Parameters + ------- + signal: FieldsContainer | Field + Signal in Pa on which to compute sharpness, as a DPF Field or Fields Container. + + """ + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal in Pa as a Field or a FieldsContainer. + """ + return self.__signal + + def process(self): + """Compute sharpness. + + Calls the appropriate DPF Sound operator to compute the sharpness of the signal. + """ + if self.__signal == None: + raise PyDpfSoundException("No signal for sharpness computation. Use Sharpness.signal.") + + self.__operator.connect(0, self.signal) + + # Runs the operator + self.__operator.run() + + # Stores outputs in the tuple variable + if type(self.signal) == FieldsContainer: + self._output = self.__operator.get_output(0, "fields_container") + elif type(self.signal) == Field: + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> FieldsContainer | Field: + """Return sharpness in a tuple of field or fields container. + + Returns + ------- + FieldsContainer | Field + Sharpness in acum. + """ + if self._output == None: + warnings.warn( + PyDpfSoundWarning("Output has not been processed yet, use Sharpness.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return sharpness as a numpy array. + + Returns + ------- + numpy.ndarray: + Array of sharpness values, in acum. + """ + output = self.get_output() + + if output == None: + return None + + if type(output) == Field: + return np.array(output.data) + + return self.convert_fields_container_to_np_array(output) + + def get_sharpness(self, channel_index: int = 0) -> np.float64: + """Return sharpness as a float. + + Returns sharpness in acum for the specified channel. + + Parameters + ---------- + channel_index: int + Index of the signal channel (0 by default) for which to return the sharpness value. + + Returns + ------- + numpy.float64 + Sharpness value in acum. + """ + if self.get_output() == None: + return None + + sharpness_data = self.get_output_as_nparray() + + # Get last channel index. + channel_max = len(sharpness_data) - 1 + + # Check that specified channel index exists. + if channel_index > channel_max: + raise PyDpfSoundException(f"Specified channel index ({channel_index}) does not exist.") + + # Return sharpness for the specified channel. + if channel_max > 0: + return sharpness_data[channel_index][0] + else: + return sharpness_data[0] diff --git a/src/ansys/dpf/sound/pydpf_sound.py b/src/ansys/dpf/sound/pydpf_sound.py new file mode 100644 index 000000000..5133486a8 --- /dev/null +++ b/src/ansys/dpf/sound/pydpf_sound.py @@ -0,0 +1,103 @@ +"""PyDpf sound interface.""" +import warnings + +from ansys.dpf.core import FieldsContainer +import numpy as np +from numpy.typing import ArrayLike + + +class PyDpfSound: + """ + Abstract mother class for PyDpf Sound. + + This is the mother of all PyDpfSound classes, should not be used as is. + """ + + def __init__(self): + """Init class PyDpfSound. + + This function inits the class by filling its attributes. + """ + self._output = None + + def plot(self): + """Plot the output. + + Nothing to plot for this class. + """ + warnings.warn(PyDpfSoundWarning("Nothing to plot.")) + return None + + def process(self): + """Process inputs. + + Nothing to process for this class. + + Returns + ------- + None + None. + """ + warnings.warn(PyDpfSoundWarning("Nothing to process.")) + return None + + def get_output(self) -> None | FieldsContainer: + """Get output. + + Nothing to output for this class. + + Returns + ------- + FieldsContainer + Empty fields container. + """ + warnings.warn(PyDpfSoundWarning("Nothing to output.")) + return self._output + + def get_output_as_nparray(self) -> ArrayLike: + """Get output as nparray. + + Nothing to output for this class. + + Returns + ------- + np.array + Empty numpy array. + """ + warnings.warn(PyDpfSoundWarning("Nothing to output.")) + return np.empty(0) + + def convert_fields_container_to_np_array(self, fc): + """Convert fields container to numpy array. + + Converts a multichannel signal contained in a DPF Fields Container into a numpy array. + + Returns + ------- + np.array + The fields container as a numpy array. + """ + num_channels = len(fc) + np_array = np.array(fc[0].data) + + if num_channels > 1: + for i in range(1, num_channels): + np_array = np.vstack((np_array, fc[i].data)) + + return np_array + + +class PyDpfSoundException(Exception): + """PyDPF Sound Exception.""" + + def __init__(self, *args: object) -> None: + """Init method.""" + super().__init__(*args) + + +class PyDpfSoundWarning(Warning): + """PyDPF Sound Warning.""" + + def __init__(self, *args: object) -> None: + """Init method.""" + super().__init__(*args) diff --git a/src/ansys/dpf/sound/server_helpers/__init__.py b/src/ansys/dpf/sound/server_helpers/__init__.py new file mode 100644 index 000000000..e97d44bdb --- /dev/null +++ b/src/ansys/dpf/sound/server_helpers/__init__.py @@ -0,0 +1,10 @@ +"""Utilities for managing the DPF server. + +Helper functions for managing the DPF server, in particular for loading +the DPF Sound plugin. +""" + +from ._connect_to_or_start_server import connect_to_or_start_server +from ._validate_dpf_sound_connection import validate_dpf_sound_connection + +__all__ = ("connect_to_or_start_server", "validate_dpf_sound_connection") diff --git a/src/ansys/dpf/sound/server_helpers/_connect_to_or_start_server.py b/src/ansys/dpf/sound/server_helpers/_connect_to_or_start_server.py new file mode 100644 index 000000000..8a7817bb2 --- /dev/null +++ b/src/ansys/dpf/sound/server_helpers/_connect_to_or_start_server.py @@ -0,0 +1,67 @@ +"""Helpers to connect to or start a DPF server with the DPF Sound plugin.""" + +import os +from typing import Any, Optional, Union + +from ansys.dpf.core import connect_to_server, load_library, start_local_server + + +def connect_to_or_start_server( + port: Optional[int] = None, ip: Optional[str] = None, ansys_path: Optional[str] = None +) -> Any: + r"""Connect to or start a DPF server with the DPF Sound plugin loaded. + + .. note:: + + If a port or IP address is set, this method tries to connect to the server specified + and the ``ansys_path`` parameter is ignored. If no parameters are set, a local server + from the latest available Ansys installation is started. + + Parameters + ---------- + port : + Port that the DPF server is listening on. + ip : + IP address for the DPF server. + ansys_path : + Root path for the Ansys installation. For example, ``C:\\Program Files\\ANSYS Inc\\v232``. + This parameter is ignored if either the port or IP address is set. + + Returns + ------- + : + DPF server. + """ + # Collect the port to connect to the server + port_in_env = os.environ.get("ANSRV_DPF_SOUND_PORT") + if port_in_env is not None: + port = int(port_in_env) + + connect_kwargs: dict[str, Union[int, str]] = {} + if port is not None: + connect_kwargs["port"] = port + if ip is not None: + connect_kwargs["ip"] = ip + + # Decide whether we start a local server or a remote server + full_path_dll = "" + if len(list(connect_kwargs.keys())) > 0: + server = connect_to_server( + **connect_kwargs, + ) + else: # pragma: no cover + server = start_local_server( + ansys_path=ansys_path, + ) + + full_path_dll = os.path.join(ansys_path, "Acoustics\\SAS\\ads\\") + + required_version = "8.0" + server.check_version( + required_version, + f"The DPF Sound plugin requires DPF Server version {required_version} " + f"(Ansys 2024 R2) or later. Your version is currently {server.version}.", + ) + + load_library(full_path_dll + "dpf_sound.dll", "dpf_sound") + return server diff --git a/src/ansys/dpf/sound/server_helpers/_validate_dpf_sound_connection.py b/src/ansys/dpf/sound/server_helpers/_validate_dpf_sound_connection.py new file mode 100644 index 000000000..274df032b --- /dev/null +++ b/src/ansys/dpf/sound/server_helpers/_validate_dpf_sound_connection.py @@ -0,0 +1,16 @@ +"""Test if DPF Sound is available.""" + +import os + +from ansys.dpf.core import Operator, connect_to_server, load_library + +DEFAULT_PORT: int = int(os.environ.get("ANSRV_DPF_SOUND_PORT", 6780)) + + +def validate_dpf_sound_connection(port=None) -> None: + """Validate that DPF Sound is available.""" + port = port if port is not None else DEFAULT_PORT + connect_to_server(port=port) + load_library("dpf_sound.dll", "dpf_sound") + Operator("load_wav_sas") + print("DPF Sound is available and running.") diff --git a/src/ansys/dpf/sound/signal_utilities/__init__.py b/src/ansys/dpf/sound/signal_utilities/__init__.py new file mode 100644 index 000000000..672c1277a --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/__init__.py @@ -0,0 +1,26 @@ +"""Signal utilities functions. + +Helper functions related to signal management. +""" + +from ._signal_utilities_parent import SignalUtilitiesParent # isort:skip +from .apply_gain import ApplyGain +from .create_sound_field import CreateSoundField +from .crop_signal import CropSignal +from .load_wav import LoadWav +from .resample import Resample +from .sum_signals import SumSignals +from .write_wav import WriteWav +from .zero_pad import ZeroPad + +__all__ = ( + "SignalUtilitiesParent", + "LoadWav", + "WriteWav", + "Resample", + "ZeroPad", + "ApplyGain", + "SumSignals", + "CropSignal", + "CreateSoundField", +) diff --git a/src/ansys/dpf/sound/signal_utilities/_signal_utilities_parent.py b/src/ansys/dpf/sound/signal_utilities/_signal_utilities_parent.py new file mode 100644 index 000000000..152dba821 --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/_signal_utilities_parent.py @@ -0,0 +1,48 @@ +"""Signal Utilities.""" +from ansys.dpf.core import Field +import matplotlib.pyplot as plt + +from ..pydpf_sound import PyDpfSound + + +class SignalUtilitiesParent(PyDpfSound): + """ + Abstract mother class for signal utilities. + + This is the mother class of all signal utilities classes, should not be used as is. + """ + + def __init__(self): + """Init. + + Init the class. + """ + super().__init__() + + def plot(self): + """Plot signals. + + Plots the resulting signals in a single figure. + """ + output = self.get_output() + + if type(output) == Field: + num_channels = 0 + field = output + else: + num_channels = len(output) + field = output[0] + + time_data = field.time_freq_support.time_frequencies.data + time_unit = field.time_freq_support.time_frequencies.unit + unit = field.unit + + for i in range(num_channels): + plt.plot(time_data, output[i].data, label="Channel {}".format(i)) + + plt.title(field.name) + plt.legend() + plt.xlabel(time_unit) + plt.ylabel(unit) + plt.grid(True) + plt.show() diff --git a/src/ansys/dpf/sound/signal_utilities/apply_gain.py b/src/ansys/dpf/sound/signal_utilities/apply_gain.py new file mode 100644 index 000000000..dfa3cd518 --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/apply_gain.py @@ -0,0 +1,171 @@ +"""Apply gain.""" + +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +from numpy import typing as npt + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class ApplyGain(SignalUtilitiesParent): + """Apply gain. + + This class applies a gain to signals. + """ + + def __init__( + self, signal: Field | FieldsContainer = None, gain: float = 0.0, gain_in_db: bool = True + ): + """Create an apply gain class. + + Parameters + ---------- + signal: + Signals on which to apply gain as a DPF Field or FieldsContainer. + gain: + Gain value in decibels (dB) or linear unit. By default, gain is specified in decibels. + gain: + If value is true, the gain is specified in dB. + If value is false, the gain is in a linear unit. + """ + super().__init__() + self.signal = signal + self.gain = gain + self.gain_in_db = gain_in_db + self.__operator = Operator("apply_gain") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal.""" + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal as a Field or a FieldsContainer + """ + return self.__signal + + @property + def gain(self): + """Gain property.""" + return self.__gain # pragma: no cover + + @gain.setter + def gain(self, new_gain: float): + """Set the new gain. + + Parameters + ---------- + new_gain: + New gain. + """ + self.__gain = new_gain + + @gain.getter + def gain(self) -> float: + """Get the gain. + + Returns + ------- + float + Gain value. + """ + return self.__gain + + @property + def gain_in_db(self): + """Gain in dB property.""" + return self.__gain_in_db # pragma: no cover + + @gain_in_db.setter + def gain_in_db(self, new_gain_in_db: bool): + """Set the new gain_in_db value. + + Parameters + ---------- + new_gain_in_db: + True to set the gain in dB, false otherwise. + """ + if type(new_gain_in_db) is not bool: + raise PyDpfSoundException( + "new_gain_in_db must be a boolean value, either True or False." + ) + + self.__gain_in_db = new_gain_in_db + + @gain_in_db.getter + def gain_in_db(self) -> bool: + """Get the gain in dB. + + Returns + ------- + bool + Boolean value that indicates if the gain is in dB. + """ + return self.__gain_in_db + + def process(self): + """Apply gain to the signal. + + Calls the appropriate DPF Sound operator to apply gain to the signal. + """ + if self.signal == None: + raise PyDpfSoundException( + "No signal on which to apply gain. Use ApplyGain.set_signal()." + ) + + self.__operator.connect(0, self.signal) + self.__operator.connect(1, float(self.gain)) + self.__operator.connect(2, bool(self.gain_in_db)) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + if type(self.signal) == FieldsContainer: + self._output = self.__operator.get_output(0, "fields_container") + elif type(self.signal) == Field: + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> FieldsContainer | Field: + """Return the signal with a gain as a fields container. + + Returns + ------- + FieldsContainer + Signal with applied gain as a FieldContainer. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use ApplyGain.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the signal with a gain as a numpy array. + + Returns + ------- + np.array + Signal with applied gain as a numpy array. + """ + output = self.get_output() + + if type(output) == Field: + return output.data + + return self.convert_fields_container_to_np_array(output) diff --git a/src/ansys/dpf/sound/signal_utilities/create_sound_field.py b/src/ansys/dpf/sound/signal_utilities/create_sound_field.py new file mode 100644 index 000000000..ed369018c --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/create_sound_field.py @@ -0,0 +1,163 @@ +"""Create a sound field.""" +import warnings + +from ansys.dpf.core import Field, Operator +import numpy as np +from numpy import typing as npt + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class CreateSoundField(SignalUtilitiesParent): + """Create a sound field. + + This class creates a DPF Field with Sound metadata from a vector. + """ + + def __init__( + self, + data: npt.ArrayLike = np.empty(0), + sampling_frequency: float = 44100.0, + unit: str = "Pa", + ): + """Create a sound field creator class. + + Parameters + ---------- + data: + Data to use to create the sound field as a 1D numpy array. + sampling_frequency: + Sampling frequency of the data. + unit: + Unit of the data. + """ + super().__init__() + self.data = data + self.sampling_frequency = sampling_frequency + self.unit = unit + self.__operator = Operator("create_field_from_vector") + + @property + def data(self): + """Data property.""" + return self.__data # pragma: no cover + + @data.setter + def data(self, data: npt.ArrayLike): + """Set the data.""" + self.__data = data + + @data.getter + def data(self) -> npt.ArrayLike: + """Get the data. + + Returns + ------- + np.array + The data as a numpy array. + """ + return self.__data + + @property + def unit(self): + """Unit property.""" + return self.__unit # pragma: no cover + + @unit.setter + def unit(self, new_unit: str): + """Set the new unit. + + Parameters + ---------- + new_unit: + New unit as a string. + """ + self.__unit = new_unit + + @unit.getter + def unit(self) -> str: + """Get the unit. + + Returns + ------- + str + The unit. + """ + return self.__unit + + @property + def sampling_frequency(self): + """Sampling frequency property.""" + return self.__sampling_frequency # pragma: no cover + + @sampling_frequency.setter + def sampling_frequency(self, new_sampling_frequency: float): + """Set the new sampling frequency. + + Parameters + ---------- + new_sampling_frequency: + New sampling frequency (in Hz). + """ + if new_sampling_frequency < 0.0: + raise PyDpfSoundException("Sampling frequency must be greater than or equal to 0.0.") + self.__sampling_frequency = new_sampling_frequency + + @sampling_frequency.getter + def sampling_frequency(self) -> float: + """Get the sampling frequency. + + Returns + ------- + float + The sampling frequency. + """ + return self.__sampling_frequency + + def process(self): + """Create the sound field. + + Calls the appropriate DPF Sound operator to create the sound field. + """ + if np.size(self.data) == 0: + raise PyDpfSoundException("No data to use. Use CreateSoundField.set_data().") + + self.__operator.connect(0, self.data.tolist()) + self.__operator.connect(1, float(self.sampling_frequency)) + self.__operator.connect(2, str(self.unit)) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> Field: + """Return the data as a field. + + Returns + ------- + Field + The data in a DPF Field. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning( + "Output has not been yet processed, use CreateSoundField.process()." + ) + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the data as a numpy array. + + Returns + ------- + np.array + The data in a numpy array. + """ + output = self.get_output() + return output.data diff --git a/src/ansys/dpf/sound/signal_utilities/crop_signal.py b/src/ansys/dpf/sound/signal_utilities/crop_signal.py new file mode 100644 index 000000000..0e1f191ed --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/crop_signal.py @@ -0,0 +1,170 @@ +"""Crop signal.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +from numpy import typing as npt + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class CropSignal(SignalUtilitiesParent): + """Crop signal. + + This class crops signals. + """ + + def __init__( + self, signal: Field | FieldsContainer = None, start_time: float = 0.0, end_time: float = 0.0 + ): + """Create an apply gain class. + + Parameters + ---------- + signal: + Signal to resample as a DPF Field or FieldsContainer. + start_time: + Start time of the part to crop, in seconds. + end_time: + End time of the part to crop, in seconds. + """ + super().__init__() + self.signal = signal + self.start_time = start_time + self.end_time = end_time + self.__operator = Operator("get_cropped_signal") + + @property + def start_time(self): + """Start time property.""" + return self.__start_time # pragma: no cover + + @start_time.setter + def start_time(self, new_start: float): + """Set the new start time. + + Parameters + ---------- + new_start: + New start time (in seconds). + """ + if new_start < 0.0: + raise PyDpfSoundException("Start time must be greater than or equal to 0.0.") + self.__start_time = new_start + + @start_time.getter + def start_time(self) -> float: + """Get the start time. + + Returns + ------- + float + The start time. + """ + return self.__start_time + + @property + def end_time(self): + """End time property.""" + return self.__end_time # pragma: no cover + + @end_time.setter + def end_time(self, new_end: bool): + """Set the new end time. + + Parameters + ---------- + new_end: + New end time (in seconds). + """ + if new_end < 0.0: + raise PyDpfSoundException("End time must be greater than or equal to 0.0.") + + if new_end < self.start_time: + raise PyDpfSoundException("End time must be greater than or equal to the start time.") + + self.__end_time = new_end + + @end_time.getter + def end_time(self) -> float: + """Get the end time. + + Returns + ------- + float + The end time. + """ + return self.__end_time + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover* + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal.""" + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal as a Field or a FieldsContainer + """ + return self.__signal + + def process(self): + """Crop the signal. + + Calls the appropriate DPF Sound operator to crop the signal. + """ + if self.signal == None: + raise PyDpfSoundException("No signal to crop. Use CropSignal.set_signal().") + + self.__operator.connect(0, self.signal) + self.__operator.connect(1, float(self.start_time)) + self.__operator.connect(2, float(self.end_time)) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + if type(self.signal) == FieldsContainer: + self._output = self.__operator.get_output(0, "fields_container") + elif type(self.signal) == Field: + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> FieldsContainer | Field: + """Return the cropped signal as a fields container. + + Returns + ------- + FieldsContainer | Field + The cropped signal in a dpf.FieldsContainer. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use CropSignal.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the cropped signal as a numpy array. + + Returns + ------- + np.array + The cropped signal in a numpy array. + """ + output = self.get_output() + + if type(output) == Field: + return output.data + + return self.convert_fields_container_to_np_array(output) diff --git a/src/ansys/dpf/sound/signal_utilities/load_wav.py b/src/ansys/dpf/sound/signal_utilities/load_wav.py new file mode 100644 index 000000000..3893fd608 --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/load_wav.py @@ -0,0 +1,109 @@ +"""Load Wav.""" + +import warnings + +from ansys.dpf.core import DataSources, FieldsContainer, Operator +from numpy import typing as npt + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class LoadWav(SignalUtilitiesParent): + """Load wav. + + This class loads wav signals. + """ + + def __init__(self, path_to_wav: str = ""): + """Create a load wav class. + + Parameters + ---------- + path_to_wav: + Path to the wav file to load. + Can be set during the instantiation of the object or with LoadWav.set_path(). + """ + super().__init__() + self.path_to_wav = path_to_wav + self.__operator = Operator("load_wav_sas") + + @property + def path_to_wav(self): + """Path to wav property.""" + return self.__path_to_wav # pragma: no cover + + @path_to_wav.setter + def path_to_wav(self, path_to_wav: str): + """Set the path of the wav to load. + + Parameters + ---------- + path_to_wav: + Path to the wav file to load. + """ + self.__path_to_wav = path_to_wav + + @path_to_wav.getter + def path_to_wav(self) -> str: + """Get the path of the wav to load. + + Returns + ------- + str + The path to the wav to load. + """ + return self.__path_to_wav + + def process(self): + """Load the wav file. + + Calls the appropriate DPF Sound operator to load the wav file. + """ + if self.path_to_wav == "": + raise PyDpfSoundException( + "Path for loading wav file is not specified. Use LoadWav.set_path." + ) + + # Loading a WAV file + data_source_in = DataSources() + + # Creating input path + data_source_in.add_file_path(self.path_to_wav, ".wav") + + # Loading wav file and storing it into a container + self.__operator.connect(0, data_source_in) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + self._output = self.__operator.get_output(0, "fields_container") + + def get_output(self) -> FieldsContainer: + """Return the loaded wav signal as a fields container. + + Returns + ------- + FieldsContainer + The loaded wav signal in a dpf.FieldsContainer. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use LoadWav.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the loaded wav signal as a numpy array. + + Returns + ------- + np.array + The loaded wav signal in a numpy array. + """ + fc = self.get_output() + + return self.convert_fields_container_to_np_array(fc) diff --git a/src/ansys/dpf/sound/signal_utilities/resample.py b/src/ansys/dpf/sound/signal_utilities/resample.py new file mode 100644 index 000000000..759558cf2 --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/resample.py @@ -0,0 +1,135 @@ +"""Resample.""" + +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +from numpy import typing as npt + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class Resample(SignalUtilitiesParent): + """Resample. + + This class resamples signals. + """ + + def __init__( + self, signal: Field | FieldsContainer = None, new_sampling_frequency: float = 44100.0 + ): + """Create a resample class. + + Parameters + ---------- + signal: + Signal to resample as a DPF Field or FieldsContainer. + new_sampling_frequency: + New sampling frequency to use + """ + super().__init__() + self.signal = signal + self.new_sampling_frequency = new_sampling_frequency + self.__operator = Operator("resample") + + @property + def new_sampling_frequency(self): + """Property new sampling frequency.""" + return self.__new_sampling_frequency # pragma: no cover + + @new_sampling_frequency.setter + def new_sampling_frequency(self, new_sampling_frequency: float): + """Set the new sampling frequency. + + Parameters + ---------- + new_sampling_frequency: + New sampling frequency. + """ + if new_sampling_frequency < 0.0: + raise PyDpfSoundException("Sampling frequency must be strictly greater than 0.0.") + + self.__new_sampling_frequency = new_sampling_frequency + + @new_sampling_frequency.getter + def new_sampling_frequency(self) -> float: + """Get the sampling frequency. + + Returns + ------- + float + The sampling frequency. + """ + return self.__new_sampling_frequency + + @property + def signal(self): + """Property signal.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal.""" + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal as a Field or a FieldsContainer + """ + return self.__signal + + def process(self): + """Resample the signal. + + Calls the appropriate DPF Sound operator to resample the signal. + """ + if self.signal == None: + raise PyDpfSoundException("No signal to resample. Use Resample.set_signal().") + + self.__operator.connect(0, self.signal) + self.__operator.connect(1, float(self.new_sampling_frequency)) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + if type(self.signal) == FieldsContainer: + self._output = self.__operator.get_output(0, "fields_container") + elif type(self.signal) == Field: + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> FieldsContainer | Field: + """Return the resampled signal as a fields container. + + Returns + ------- + FieldsContainer + The resampled signal in a dpf.FieldsContainer. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use Resample.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the resampled signal as a numpy array. + + Returns + ------- + np.array + The resampled signal in a numpy array. + """ + output = self.get_output() + + if type(output) == Field: + return output.data + + return self.convert_fields_container_to_np_array(output) diff --git a/src/ansys/dpf/sound/signal_utilities/sum_signals.py b/src/ansys/dpf/sound/signal_utilities/sum_signals.py new file mode 100644 index 000000000..7ecced8ee --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/sum_signals.py @@ -0,0 +1,93 @@ +"""Sum signals.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +from numpy import typing as npt + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class SumSignals(SignalUtilitiesParent): + """Sum signals. + + This class sum signals. + """ + + def __init__(self, signals: FieldsContainer = None): + """Create a sum signal class. + + Parameters + ---------- + signals: + Input signals to sum, each field of the signal will be summed. + """ + super().__init__() + self.signals = signals + self.__operator = Operator("sum_signals") + + @property + def signals(self): + """Signals property.""" + return self.__signals # pragma: no cover + + @signals.setter + def signals(self, signals: FieldsContainer): + """Set the signals to sum.""" + self.__signals = signals + + @signals.getter + def signals(self) -> FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer + The signal as a FieldsContainer + """ + return self.__signals + + def process(self): + """Sum signals. + + Calls the appropriate DPF Sound operator to sum signals. + """ + if self.signals == None: + raise PyDpfSoundException( + "No signal on which to apply gain. Use SumSignals.set_signal()." + ) + + self.__operator.connect(0, self.signals) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> Field: + """Return the summed signals as a field. + + Returns + ------- + Field + The summed signal in a Field. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use SumSignals.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the signal with a gain as a numpy array. + + Returns + ------- + np.array + The summed signal in a numpy array. + """ + output = self.get_output() + return output.data diff --git a/src/ansys/dpf/sound/signal_utilities/write_wav.py b/src/ansys/dpf/sound/signal_utilities/write_wav.py new file mode 100644 index 000000000..245e2e134 --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/write_wav.py @@ -0,0 +1,151 @@ +"""Write Wav.""" + +import warnings + +from ansys.dpf.core import DataSources, FieldsContainer, Operator + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class WriteWav(SignalUtilitiesParent): + """Write wav. + + This class writes wav signals. + """ + + def __init__( + self, signal: FieldsContainer = None, path_to_write: str = "", bit_depth: str = "float32" + ): + """Create a write wav class. + + Parameters + ---------- + signal: + Signal to save: fields_container with each channel as a field. + path_to_write: + Path where to write the wav file. + Can be set during the instantiation of the object or with LoadWav.set_path(). + bit_depth: + Bit depth. Supported values are: 'float32', 'int32', 'int16', 'int8'. + This means that the samples will be respectively coded into the wav file + using 32 bits (32-bit IEEE Float), 32 bits (int), 16 bits (int) or 8 bits (int). + """ + super().__init__() + self.path_to_write = path_to_write + self.signal = signal + self.bit_depth = bit_depth + self.__operator = Operator("write_wav_sas") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: FieldsContainer): + """Setter for the signal. + + Sets the value of the signal to write in memory. + """ + self.__signal = signal + + @signal.getter + def signal(self) -> FieldsContainer: + """Getter for the signal. + + Returns + ------- + FieldsContainer + The signal that is to be written in memory as a FieldsContainer. + """ + return self.__signal + + @property + def bit_depth(self): + """Bit depth property.""" + return self.__bit_depth # pragma: no cover + + @bit_depth.setter + def bit_depth(self, bit_depth: str): + """Setter for the bit depth. + + Sets the bit depth. + """ + if ( + bit_depth != "int8" + and bit_depth != "int16" + and bit_depth != "int32" + and bit_depth != "float32" + ): + raise PyDpfSoundException( + "Invalid bit depth, accepted values are 'float32', 'int32', 'int16', 'int8'." + ) + + self.__bit_depth = bit_depth + + @bit_depth.getter + def bit_depth(self) -> str: + """Getter for the bit depth. + + Returns + ------- + str + The bit depth. + """ + return self.__bit_depth + + @property + def path_to_write(self): + """Path to write property.""" + return self.__path_to_write # pragma: no cover + + @path_to_write.setter + def path_to_write(self, path_to_write: str): + """Setter for the write path. + + Sets the path for writing the signal in memory. + """ + self.__path_to_write = path_to_write + + @path_to_write.getter + def path_to_write(self) -> str: + """Getter for the write path. + + Returns + ------- + str + The path for writing the signal in memory. + """ + return self.__path_to_write + + def process(self): + """Write the wav file. + + Calls the appropriate DPF Sound operator to writes the wav file. + """ + if self.path_to_write == "": + raise PyDpfSoundException( + "Path for write wav file is not specified. Use WriteWav.set_path." + ) + + if self.signal == None: + raise PyDpfSoundException( + "No signal is specified for writing, use WriteWav.set_signal." + ) + + data_source_out = DataSources() + data_source_out.add_file_path(self.path_to_write, ".wav") + + self.__operator.connect(0, self.signal) + self.__operator.connect(1, data_source_out) + self.__operator.connect(2, self.bit_depth) + + self.__operator.run() + + def plot(self): + """Plot the output. + + Nothing to plot for this class. + """ + warnings.warn(PyDpfSoundWarning("Nothing to plot.")) diff --git a/src/ansys/dpf/sound/signal_utilities/zero_pad.py b/src/ansys/dpf/sound/signal_utilities/zero_pad.py new file mode 100644 index 000000000..41deb2322 --- /dev/null +++ b/src/ansys/dpf/sound/signal_utilities/zero_pad.py @@ -0,0 +1,132 @@ +"""Zero Pad.""" +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +from numpy import typing as npt + +from . import SignalUtilitiesParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class ZeroPad(SignalUtilitiesParent): + """Zero pad. + + This class zero pads (adds zeros at the end of) signals. + """ + + def __init__(self, signal: Field | FieldsContainer = None, duration_zeros: float = 0.0): + """Create a zero pad class. + + Parameters + ---------- + signal: + Signal to resample as a DPF Field or FieldsContainer. + duration_zeros: + Duration, in seconds, of the zeros to append to the input signal + """ + super().__init__() + self.signal = signal + self.duration_zeros = duration_zeros + self.__operator = Operator("append_zeros_to_signal") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal.""" + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + FieldsContainer | Field + The signal as a Field or a FieldsContainer + """ + return self.__signal + + @property + def duration_zeros(self): + """Duration zeros property.""" + return self.__duration_zeros # pragma: no cover + + @duration_zeros.setter + def duration_zeros(self, new_duration_zeros: float): + """Set the new duration of zeros. + + Parameters + ---------- + new_duration_zeros: + New duration for the zero padding (in seconds). + """ + if new_duration_zeros < 0.0: + raise PyDpfSoundException("Zero duration must be strictly greater than 0.0.") + + self.__duration_zeros = new_duration_zeros + + @duration_zeros.getter + def duration_zeros(self) -> float: + """Get the sampling frequency. + + Returns + ------- + float + The sampling frequency. + """ + return self.__duration_zeros + + def process(self): + """Zero pad the signal. + + Calls the appropriate DPF Sound operator to append zeros to the signal. + """ + if self.signal == None: + raise PyDpfSoundException("No signal to zero-pad. Use ZeroPad.set_signal().") + + self.__operator.connect(0, self.signal) + self.__operator.connect(1, float(self.duration_zeros)) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + if type(self.signal) == FieldsContainer: + self._output = self.__operator.get_output(0, "fields_container") + elif type(self.signal) == Field: + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> FieldsContainer | Field: + """Return the zero-padded signal as a fields container. + + Returns + ------- + FieldsContainer + The zero-padded signal signal in a dpf.FieldsContainer. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use ZeroPad.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the zero-padded signal as a numpy array. + + Returns + ------- + np.array + The zero-padded signal signal in a np.array. + """ + output = self.get_output() + + if type(output) == Field: + return output.data + + return self.convert_fields_container_to_np_array(output) diff --git a/src/ansys/dpf/sound/spectrogram_processing/__init__.py b/src/ansys/dpf/sound/spectrogram_processing/__init__.py new file mode 100644 index 000000000..d91d66efd --- /dev/null +++ b/src/ansys/dpf/sound/spectrogram_processing/__init__.py @@ -0,0 +1,11 @@ +"""Spectrogram processing classes. + +Helper functions related to spectrogram processing. +""" + +from ._spectrogram_processing_parent import SpectrogramProcessingParent # isort:skip +from .isolate_orders import IsolateOrders +from .istft import Istft +from .stft import Stft + +__all__ = "SpectrogramProcessingParent", "Stft", "Istft", "IsolateOrders" diff --git a/src/ansys/dpf/sound/spectrogram_processing/_spectrogram_processing_parent.py b/src/ansys/dpf/sound/spectrogram_processing/_spectrogram_processing_parent.py new file mode 100644 index 000000000..549f098f3 --- /dev/null +++ b/src/ansys/dpf/sound/spectrogram_processing/_spectrogram_processing_parent.py @@ -0,0 +1,17 @@ +"""Spectrogram Processing.""" +from ..pydpf_sound import PyDpfSound + + +class SpectrogramProcessingParent(PyDpfSound): + """ + Abstract mother class for spectrogram processing. + + This is the mother class of all spectrogram processing, should not be used as is. + """ + + def __init__(self): + """Init. + + Init the class. + """ + super().__init__() diff --git a/src/ansys/dpf/sound/spectrogram_processing/isolate_orders.py b/src/ansys/dpf/sound/spectrogram_processing/isolate_orders.py new file mode 100644 index 000000000..106310e00 --- /dev/null +++ b/src/ansys/dpf/sound/spectrogram_processing/isolate_orders.py @@ -0,0 +1,330 @@ +"""Isolate orders of a signal.""" + +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import matplotlib.pyplot as plt +from numpy import typing as npt + +from . import SpectrogramProcessingParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class IsolateOrders(SpectrogramProcessingParent): + """Isolate orders of a signal. + + This class isolates the order of a given signal that has an associated RPM profile. + """ + + def __init__( + self, + signal: FieldsContainer | Field = None, + rpm_profile: Field = None, + orders: list = None, + fft_size: int = 1024, + window_type: str = "HANN", + window_overlap: float = 0.5, + width_selection: int = 10, + ): + """Create an Istft class. + + Parameters + ---------- + signal: + The input signal(s) as a fields container or a field on which to isolate orders. + rpm_profile: + The RPM signal associated with the time signals, as a field. + It is assumed that the signal's unit is 'rpm': if this is not the case, + inaccurate behavior might occur during the conversion from RPM to frequency. + orders + List of the order numbers you want to isolate. Must contain at least one value. + fft_size + Size (as an integer) of the FFT used to compute the STFT. Default is 1024. + window_type + The window used for the FFT computation, as a string. + Allowed input strings are : + 'HANNING', 'BLACKMANHARRIS', 'HANN','BLACKMAN', 'HAMMING', 'KAISER', 'BARTLETT' and + 'RECTANGULAR'. + If no parameter is specified, the default value is 'HANNING'. + window_overlap: + The overlap value between two successive FFT computations (value between 0 and 1). + 0 means no overlap, 0.5 means 50 % overlap. + If no parameter is specified, default value is 0.5. + width_selection + The width, in Hz, of the area used to select each individual order. + Note that its precision depends on the FFT size. Default value is 10 Hz. + """ + super().__init__() + self.signal = signal + self.rpm_profile = rpm_profile + self.orders = orders + self.fft_size = fft_size + self.window_type = window_type + self.window_overlap = window_overlap + self.width_selection = width_selection + self.__operator = Operator("isolate_orders") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal.""" + self.__signal = signal + + @signal.getter + def signal(self) -> Field | FieldsContainer: + """Get the signal. + + Returns + ------- + Field + The signal as a Field or a FieldsContainer. + """ + return self.__signal + + @property + def rpm_profile(self): + """RPM Profile property.""" + return self.__rpm_profile # pragma: no cover + + @rpm_profile.getter + def rpm_profile(self) -> Field: + """Get the RPM profile. + + Returns + ------- + Field + The RPM profile. + """ + return self.__rpm_profile + + @rpm_profile.setter + def rpm_profile(self, rpm_profile: Field): + """Set the RPM Profile.""" + self.__rpm_profile = rpm_profile + + @property + def orders(self): + """Orders property.""" + return self.__orders # pragma: no cover + + @orders.getter + def orders(self) -> Field: + """Get the orders. + + Returns + ------- + list + The orders. + """ + return self.__orders + + @orders.setter + def orders(self, orders: Field | list): + """Set the orders.""" + if type(orders) == list: + f = Field() + f.append(orders, 1) + self.__orders = f + elif type(orders): + self.__orders = orders + + @property + def fft_size(self): + """FFT size property.""" + return self.__fft_size # pragma: no cover + + @fft_size.setter + def fft_size(self, fft_size): + """Set the FFT size.""" + if fft_size < 0: + raise PyDpfSoundException("FFT size must be greater than 0.0.") + self.__fft_size = fft_size + + @fft_size.getter + def fft_size(self) -> float: + """Get the FFT size. + + Returns + ------- + float + The FFT size. + """ + return self.__fft_size + + @property + def window_type(self): + """Window type property.""" + return self.__window_type # pragma: no cover + + @window_type.setter + def window_type(self, window_type): + """Set the window type.""" + if ( + window_type != "BLACKMANHARRIS" + and window_type != "HANN" + and window_type != "HAMMING" + and window_type != "HANNING" + and window_type != "KAISER" + and window_type != "BARTLETT" + and window_type != "RECTANGULAR" + ): + raise PyDpfSoundException( + "Invalid window type, accepted values are 'HANNING', 'BLACKMANHARRIS', 'HANN', \ + 'BLACKMAN','HAMMING', 'KAISER', 'BARTLETT', 'RECTANGULAR'." + ) + + self.__window_type = window_type + + @window_type.getter + def window_type(self) -> str: + """Get the window type. + + Returns + ------- + str + The window type. + """ + return self.__window_type + + @property + def window_overlap(self): + """Window overlap property.""" + return self.__window_overlap # pragma: no cover + + @window_overlap.setter + def window_overlap(self, window_overlap): + """Set the window overlap.""" + if window_overlap < 0.0 or window_overlap > 1.0: + raise PyDpfSoundException("Window overlap must be between 0.0 and 1.0.") + + self.__window_overlap = window_overlap + + @window_overlap.getter + def window_overlap(self) -> float: + """Get the window overlap. + + Returns + ------- + float + The window overlap. + """ + return self.__window_overlap + + @property + def width_selection(self): + """Width selection property.""" + return self.__width_selection # pragma: no cover + + @width_selection.setter + def width_selection(self, widt_selection): + """Set the width selection.""" + if widt_selection < 0: + raise PyDpfSoundException("Width selection must be greater than 0.0.") + self.__width_selection = widt_selection + + @width_selection.getter + def width_selection(self) -> int: + """Get the width selection. + + Returns + ------- + int + The width selection. + """ + return self.__width_selection + + def process(self): + """Isolate the orders. + + Calls the appropriate DPF Sound operator to isolate the orders of the signal. + """ + if self.signal == None: + raise PyDpfSoundException("No signal for order isolation. Use IsolateOrder.signal.") + + if self.rpm_profile == None: + raise PyDpfSoundException( + "No RPM profile for order isolation. Use IsolateOrder.rpm_profile." + ) + + if self.orders == None: + raise PyDpfSoundException("No orders for order isolation. Use IsolateOrder.orders.") + + self.__operator.connect(0, self.signal) + self.__operator.connect(1, self.rpm_profile) + self.__operator.connect(2, self.orders) + self.__operator.connect(3, self.fft_size) + self.__operator.connect(4, self.window_type) + self.__operator.connect(5, self.window_overlap) + self.__operator.connect(6, self.width_selection) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + if type(self.signal) == FieldsContainer: + self._output = self.__operator.get_output(0, "fields_container") + elif type(self.signal) == Field: + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> Field | FieldsContainer: + """Return the temporal signal of the isolated orders as a Field or Fields Container. + + Returns + ------- + Field | FieldsContainer + The signal resulting from the order isolation as a DPF Field or FieldsContainer. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use IsolateOrders.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the temporal signal of the isolated orders as a numpy array. + + Returns + ------- + np.array + The resulting from the order isolation as a numpy array. + """ + output = self.get_output() + + if type(output) == Field: + return output.data + + return self.convert_fields_container_to_np_array(output) + + def plot(self): + """Plot signals. + + Plots the signal after order isolation. + """ + output = self.get_output() + + if type(output) == Field: + num_channels = 0 + field = output + else: + num_channels = len(output) + field = output[0] + + time_data = field.time_freq_support.time_frequencies.data + time_unit = field.time_freq_support.time_frequencies.unit + unit = field.unit + + for i in range(num_channels): + plt.plot(time_data, output[i].data, label="Channel {}".format(i)) + + plt.title(field.name) + plt.legend() + plt.xlabel(time_unit) + plt.ylabel(unit) + plt.grid(True) + plt.show() diff --git a/src/ansys/dpf/sound/spectrogram_processing/istft.py b/src/ansys/dpf/sound/spectrogram_processing/istft.py new file mode 100644 index 000000000..fbcbed026 --- /dev/null +++ b/src/ansys/dpf/sound/spectrogram_processing/istft.py @@ -0,0 +1,129 @@ +"""Inverse Short-time Fourier Transform.""" + +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import matplotlib.pyplot as plt +import numpy as np +from numpy import typing as npt + +from . import SpectrogramProcessingParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class Istft(SpectrogramProcessingParent): + """Inverse Short-time Fourier Transform. + + This class computes the ISTFT (Inverse Short-time Fourier transform) of a signal. + """ + + def __init__(self, stft: FieldsContainer = None): + """Create an Istft class. + + Parameters + ---------- + stft: + A FieldsContainer containing an STFT computed with the Stft class. + """ + super().__init__() + self.stft = stft + self.__operator = Operator("compute_istft") + + @property + def stft(self): + """STFT property.""" + return self.__stft # pragma: no cover + + @stft.setter + def stft(self, stft: FieldsContainer): + """Set the STFT.""" + if type(stft) != FieldsContainer and stft != None: + raise PyDpfSoundException("Input must be a Fields container.") + + if stft != None and ( + not stft.has_label("time") + or not stft.has_label("complex") + or not stft.has_label("channel_number") + ): + raise PyDpfSoundException( + "STFT is in the wrong format, make sure it has been computed with the Stft class." + ) + + self.__stft = stft + + @stft.getter + def stft(self) -> FieldsContainer: + """Get the STFT. + + Returns + ------- + FieldsContainer + The STFT as a FieldsContainer. + """ + return self.__stft + + def process(self): + """Compute the ISTFT. + + Calls the appropriate DPF Sound operator to compute the Inverse STFT of the STFT. + """ + if self.stft == None: + raise PyDpfSoundException("No STFT input for ISTFT computation. Use Istft.stft.") + + self.__operator.connect(0, self.stft) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + self._output = self.__operator.get_output(0, "field") + + def get_output(self) -> Field: + """Return the ISTFT resulting signal as a field. + + Returns + ------- + Field + The signal resulting from the ISTFT as a DPF Field. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use Istft.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the ISTFT resulting signal as a numpy array. + + Returns + ------- + np.array + The ISTFT resulting signal in a numpy array. + """ + output = self.get_output() + out_as_np_array = output.data + + # return out_as_np_array + return np.transpose(out_as_np_array) + + def plot(self): + """Plot signals. + + Plots the signal resulting from ISTFT. + """ + output = self.get_output() + field = output + + time_data = output.time_freq_support.time_frequencies.data + time_unit = output.time_freq_support.time_frequencies.unit + unit = field.unit + plt.plot(time_data, field.data, label="Signal") + + plt.title(field.name) + plt.legend() + plt.xlabel(time_unit) + plt.ylabel(unit) + plt.grid(True) + plt.show() diff --git a/src/ansys/dpf/sound/spectrogram_processing/stft.py b/src/ansys/dpf/sound/spectrogram_processing/stft.py new file mode 100644 index 000000000..e2c65a2c3 --- /dev/null +++ b/src/ansys/dpf/sound/spectrogram_processing/stft.py @@ -0,0 +1,283 @@ +"""Short-time Fourier Transform.""" + +import warnings + +from ansys.dpf.core import Field, FieldsContainer, Operator +import matplotlib.pyplot as plt +import numpy as np +from numpy import typing as npt + +from . import SpectrogramProcessingParent +from ..pydpf_sound import PyDpfSoundException, PyDpfSoundWarning + + +class Stft(SpectrogramProcessingParent): + """Short-time Fourier Transform. + + This class computes the STFT (Short-time Fourier transform) of a signal. + """ + + def __init__( + self, + signal: Field | FieldsContainer = None, + fft_size: float = 2048, + window_type: str = "HANN", + window_overlap: float = 0.5, + ): + """Create an STFT class. + + Parameters + ---------- + signal: + Mono signal on which to compute the STFT as a DPF Field or Fields Container. + fft_size: + Size (as an integer) of the FFT to compute the STFT. + Use a power of 2 for better performance. + window_type: + The window used for the FFT computation, as a string. + Allowed input strings are : + 'HANNING', 'BLACKMANHARRIS', 'HANN','BLACKMAN', 'HAMMING', 'KAISER', 'BARTLETT' and + 'RECTANGULAR'. + If no parameter is specified, the default value is 'HANNING'. + window_overlap: + The overlap value between two successive FFT computations (value between 0 and 1). + 0 means no overlap, 0.5 means 50 % overlap. + If no parameter is specified, default value is 0.5. + """ + super().__init__() + self.signal = signal + self.fft_size = fft_size + self.window_overlap = window_overlap + self.window_type = window_type + self.__operator = Operator("compute_stft") + + @property + def signal(self): + """Signal property.""" + return self.__signal # pragma: no cover + + @signal.setter + def signal(self, signal: Field | FieldsContainer): + """Set the signal.""" + if type(signal) == FieldsContainer: + if len(signal) > 1: + raise PyDpfSoundException( + "Input as FieldsContainer can only have one Field (mono signal)." + ) + else: + self.__signal = signal[0] + else: + self.__signal = signal + + @signal.getter + def signal(self) -> Field: + """Get the signal. + + Returns + ------- + Field + The signal as a Field. + """ + return self.__signal + + @property + def fft_size(self): + """FFT size property.""" + return self.__fft_size # pragma: no cover + + @fft_size.setter + def fft_size(self, fft_size): + """Set the FFT size.""" + if fft_size < 0: + raise PyDpfSoundException("FFT size must be greater than 0.0.") + self.__fft_size = fft_size + + @fft_size.getter + def fft_size(self) -> float: + """Get the FFT size. + + Returns + ------- + float + The FFT size. + """ + return self.__fft_size + + @property + def window_type(self): + """Window type property.""" + return self.__window_type # pragma: no cover + + @window_type.setter + def window_type(self, window_type): + """Set the window type.""" + if ( + window_type != "BLACKMANHARRIS" + and window_type != "HANN" + and window_type != "HAMMING" + and window_type != "HANNING" + and window_type != "KAISER" + and window_type != "BARTLETT" + and window_type != "RECTANGULAR" + ): + raise PyDpfSoundException( + "Invalid window type, accepted values are 'HANNING', 'BLACKMANHARRIS', 'HANN', \ + 'BLACKMAN','HAMMING', 'KAISER', 'BARTLETT', 'RECTANGULAR'." + ) + + self.__window_type = window_type + + @window_type.getter + def window_type(self) -> float: + """Get the window type. + + Returns + ------- + str + The window type. + """ + return self.__window_type + + @property + def window_overlap(self): + """Window overlap property.""" + return self.__window_overlap # pragma: no cover + + @window_overlap.setter + def window_overlap(self, window_overlap): + """Set the window overlap.""" + if window_overlap < 0.0 or window_overlap > 1.0: + raise PyDpfSoundException("Window overlap must be between 0.0 and 1.0.") + + self.__window_overlap = window_overlap + + @window_overlap.getter + def window_overlap(self) -> float: + """Get the fft size. + + Returns + ------- + float + The window overlap. + """ + return self.__window_overlap + + def process(self): + """Compute the STFT. + + Calls the appropriate DPF Sound operator to compute the STFT of the signal. + """ + if self.signal == None: + raise PyDpfSoundException("No signal for STFT. Use Stft.signal.") + + self.__operator.connect(0, self.signal) + self.__operator.connect(1, int(self.fft_size)) + self.__operator.connect(2, str(self.window_type)) + self.__operator.connect(3, float(self.window_overlap)) + + # Runs the operator + self.__operator.run() + + # Stores output in the variable + self._output = self.__operator.get_output(0, "fields_container") + + def get_output(self) -> FieldsContainer: + """Return the STFT as a fields container. + + Returns + ------- + FieldsContainer + The STFT of the signal in a DPF FieldsContainer. + """ + if self._output == None: + # Computing output if needed + warnings.warn( + PyDpfSoundWarning("Output has not been yet processed, use Stft.process().") + ) + + return self._output + + def get_output_as_nparray(self) -> npt.ArrayLike: + """Return the STFT of the signal as a numpy array. + + Returns + ------- + np.array + The STFT of the signal in a numpy array. + """ + output = self.get_output() + + num_time_index = len(output.get_available_ids_for_label("time")) + + f1 = output.get_field({"complex": 0, "time": 0, "channel_number": 0}) + f2 = output.get_field({"complex": 1, "time": 0, "channel_number": 0}) + + out_as_np_array = f1.data + 1j * f2.data + for i in range(1, num_time_index): + f1 = output.get_field({"complex": 0, "time": i, "channel_number": 0}) + f2 = output.get_field({"complex": 1, "time": i, "channel_number": 0}) + tmp_arr = f1.data + 1j * f2.data + out_as_np_array = np.vstack((out_as_np_array, tmp_arr)) + + # return out_as_np_array + return np.transpose(out_as_np_array) + + def get_stft_magnitude_as_nparray(self) -> npt.ArrayLike: + """Return the amplitude of the STFT. + + Returns + ------- + np.array + The Amplitude of the STFT of the signal in a numpy array. + """ + output = self.get_output_as_nparray() + return np.absolute(output) + + def get_stft_phase_as_nparray(self) -> npt.ArrayLike: + """Return the phase of the STFT. + + Returns + ------- + np.array + The Phase of the STFT of the signal in a numpy array. + """ + output = self.get_output_as_nparray() + return np.arctan2(np.imag(output), np.real(output)) + + def plot(self): + """Plot signals. + + Plots the STFT amplitude and the associated phase. + """ + out = self.get_output_as_nparray() + + # Extracting first half of the STFT (second half is symmetrical) + half_nfft = int(np.shape(out)[0] / 2) + 1 + magnitude = self.get_stft_magnitude_as_nparray() + magnitude = 20 * np.log10(magnitude[0:half_nfft, :]) + phase = self.get_stft_phase_as_nparray() + phase = phase[0:half_nfft, :] + fs = 1.0 / ( + self.signal.time_freq_support.time_frequencies.data[1] + - self.signal.time_freq_support.time_frequencies.data[0] + ) + time_step = np.floor(self.fft_size * (1.0 - self.window_overlap) + 0.5) / fs + num_time_index = len(self.get_output().get_available_ids_for_label("time")) + + # Boundaries of the plot + extent = [0, time_step * num_time_index, 0.0, fs / 2.0] + + # Plotting + f, (ax1, ax2) = plt.subplots(2, 1, sharex=True) + p = ax1.imshow(magnitude, origin="lower", aspect="auto", cmap="jet", extent=extent) + f.colorbar(p, ax=ax1, label="dB") + ax1.set_title("Amplitude") + ax1.set_ylabel("Frequency (Hz)") + p = ax2.imshow(phase, origin="lower", aspect="auto", cmap="jet", extent=extent) + f.colorbar(p, ax=ax2, label="rad") + ax2.set_title("Phase") + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Frequency (Hz)") + + f.suptitle("STFT") + plt.show() diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..9f0a737ad --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,37 @@ +import os + +from ansys.dpf.core import connect_to_server, load_library +import pytest + +CONTAINER_SERVER_PORT = 6780 +STR_DPF_SOUND = "dpf_sound.dll" +STR_DPF_SOUND_DLL = "dpf_sound.dll" + + +def pytest_configure(): + pytest.data_path_flute_in_container = "C:\\data\\flute.wav" + pytest.data_path_flute2_in_container = "C:\\data\\flute2.wav" + pytest.data_path_sharp_noise_in_container = "C:\\data\\sharp_noise.wav" + pytest.data_path_sharper_noise_in_container = "C:\\data\\sharper_noise.wav" + pytest.data_path_rough_noise_in_container = "C:\\data\\rough_noise.wav" + pytest.data_path_rough_tone_in_container = "C:\\data\\rough_tone.wav" + pytest.data_path_fluctuating_noise_in_container = "C:\\data\\fluctuating_noise.wav" + pytest.data_path_fluctuating_tone_in_container = "C:\\data\\fluctuating_tone.wav" + pytest.data_path_white_noise_in_container = "C:\\data\\white_noise.wav" + pytest.data_path_accel_with_rpm_in_container = "C:\\data\\accel_with_rpm.wav" + + +@pytest.fixture(scope="session") +def dpf_sound_test_server(): + port_in_env = os.environ.get("ANSRV_DPF_SOUND_PORT") + if port_in_env is not None: + port = int(port_in_env) + else: + port = CONTAINER_SERVER_PORT + + # Connecting to server + server = connect_to_server(port=port) + + # Loading DPF Sound + load_library(STR_DPF_SOUND_DLL, STR_DPF_SOUND) + yield server diff --git a/tests/data/accel_with_rpm.wav b/tests/data/accel_with_rpm.wav new file mode 100644 index 000000000..51a264674 Binary files /dev/null and b/tests/data/accel_with_rpm.wav differ diff --git a/tests/data/fluctuating_noise.wav b/tests/data/fluctuating_noise.wav new file mode 100644 index 000000000..bccd380b1 Binary files /dev/null and b/tests/data/fluctuating_noise.wav differ diff --git a/tests/data/fluctuating_tone.wav b/tests/data/fluctuating_tone.wav new file mode 100644 index 000000000..5abeca8df Binary files /dev/null and b/tests/data/fluctuating_tone.wav differ diff --git a/tests/data/flute.wav b/tests/data/flute.wav new file mode 100644 index 000000000..9e49a3f66 Binary files /dev/null and b/tests/data/flute.wav differ diff --git a/tests/data/flute2.wav b/tests/data/flute2.wav new file mode 100644 index 000000000..6d2cc2a5d Binary files /dev/null and b/tests/data/flute2.wav differ diff --git a/tests/data/rough_noise.wav b/tests/data/rough_noise.wav new file mode 100644 index 000000000..9cb59a36b Binary files /dev/null and b/tests/data/rough_noise.wav differ diff --git a/tests/data/rough_tone.wav b/tests/data/rough_tone.wav new file mode 100644 index 000000000..76c0f876a Binary files /dev/null and b/tests/data/rough_tone.wav differ diff --git a/tests/data/sharp_noise.wav b/tests/data/sharp_noise.wav new file mode 100644 index 000000000..a3327bf3d Binary files /dev/null and b/tests/data/sharp_noise.wav differ diff --git a/tests/data/sharper_noise.wav b/tests/data/sharper_noise.wav new file mode 100644 index 000000000..751f31177 Binary files /dev/null and b/tests/data/sharper_noise.wav differ diff --git a/tests/data/white_noise.wav b/tests/data/white_noise.wav new file mode 100644 index 000000000..ebd920e4c Binary files /dev/null and b/tests/data/white_noise.wav differ diff --git a/tests/test_example_helpers.py b/tests/test_example_helpers.py new file mode 100644 index 000000000..028ccb469 --- /dev/null +++ b/tests/test_example_helpers.py @@ -0,0 +1,50 @@ +from ansys.dpf.sound.examples_helpers import ( + get_absolute_path_for_fluctuating_noise_wav, + get_absolute_path_for_fluctuating_tone_wav, + get_absolute_path_for_flute2_wav, + get_absolute_path_for_flute_wav, + get_absolute_path_for_rough_noise_wav, + get_absolute_path_for_rough_tone_wav, + get_absolute_path_for_sharp_noise_wav, + get_absolute_path_for_sharper_noise_wav, +) + + +def test_data_path_flute_wav(): + p = get_absolute_path_for_flute_wav() + assert p == "C:\\data\\flute.wav" + + +def test_data_path_flute2_wav(): + p = get_absolute_path_for_flute2_wav() + assert p == "C:\\data\\flute2.wav" + + +def test_data_path_sharp_noise_wav(): + p = get_absolute_path_for_sharp_noise_wav() + assert p == "C:\\data\\sharp_noise.wav" + + +def test_data_path_sharper_noise_wav(): + p = get_absolute_path_for_sharper_noise_wav() + assert p == "C:\\data\\sharper_noise.wav" + + +def test_data_path_rough_noise_wav(): + p = get_absolute_path_for_rough_noise_wav() + assert p == "C:\\data\\rough_noise.wav" + + +def test_data_path_rough_tone_wav(): + p = get_absolute_path_for_rough_tone_wav() + assert p == "C:\\data\\rough_tone.wav" + + +def test_data_path_fluctuating_noise_wav(): + p = get_absolute_path_for_fluctuating_noise_wav() + assert p == "C:\\data\\fluctuating_noise.wav" + + +def test_data_path_fluctuating_tone_wav(): + p = get_absolute_path_for_fluctuating_tone_wav() + assert p == "C:\\data\\fluctuating_tone.wav" diff --git a/tests/test_metadata.py b/tests/test_metadata.py new file mode 100644 index 000000000..53dca5446 --- /dev/null +++ b/tests/test_metadata.py @@ -0,0 +1,5 @@ +from ansys.dpf.sound import __version__ + + +def test_pkg_version(): + assert __version__ == "0.1.dev0" diff --git a/tests/test_pydpf_sound.py b/tests/test_pydpf_sound.py new file mode 100644 index 000000000..2433daa6c --- /dev/null +++ b/tests/test_pydpf_sound.py @@ -0,0 +1,47 @@ +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSound, PyDpfSoundWarning + + +@pytest.mark.dependency() +def test_pydpf_sound_instanciate(): + pydpf_sound = PyDpfSound() + assert pydpf_sound != None + + +@pytest.mark.dependency(depends=["test_pydpf_sound_instanciate"]) +def test_pydpf_sound_process(): + pydpf_sound = PyDpfSound() + with pytest.warns(PyDpfSoundWarning, match="Nothing to process."): + pydpf_sound.process() + assert pydpf_sound.process() == None + + +@pytest.mark.dependency(depends=["test_pydpf_sound_instanciate"]) +def test_pydpf_sound_plot(): + pydpf_sound = PyDpfSound() + with pytest.warns(PyDpfSoundWarning, match="Nothing to plot."): + pydpf_sound.plot() + + assert pydpf_sound.plot() == None + + +@pytest.mark.dependency(depends=["test_pydpf_sound_instanciate"]) +def test_pydpf_sound_get_output(): + pydpf_sound = PyDpfSound() + with pytest.warns(PyDpfSoundWarning, match="Nothing to output."): + pydpf_sound.get_output() + out = pydpf_sound.get_output() + assert out == None + + +@pytest.mark.dependency(depends=["test_pydpf_sound_instanciate"]) +def test_pydpf_sound_get_output_as_nparray(): + pydpf_sound = PyDpfSound() + with pytest.warns(PyDpfSoundWarning, match="Nothing to output."): + pydpf_sound.get_output_as_nparray() + out = pydpf_sound.get_output_as_nparray() + assert type(out) == type(np.empty(0)) + assert np.size(out) == 0 + assert np.shape(out) == (0,) diff --git a/tests/test_server_helpers.py b/tests/test_server_helpers.py new file mode 100644 index 000000000..32c8ac9bc --- /dev/null +++ b/tests/test_server_helpers.py @@ -0,0 +1,10 @@ +from ansys.dpf.sound.server_helpers import connect_to_or_start_server, validate_dpf_sound_connection + + +def test_validate_dpf_sound_connection(): + validate_dpf_sound_connection() + + +def test_connect_to_or_start_server(): + s = connect_to_or_start_server(port="6780", ip="127.0.0.1") + print(s) diff --git a/tests/tests_psychoacoustics/test_psychoacoustics_fluctuation_strength.py b/tests/tests_psychoacoustics/test_psychoacoustics_fluctuation_strength.py new file mode 100644 index 000000000..095f8941d --- /dev/null +++ b/tests/tests_psychoacoustics/test_psychoacoustics_fluctuation_strength.py @@ -0,0 +1,395 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.psychoacoustics import FluctuationStrength +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav + +EXP_FS_1 = 1.0416046380996704 +EXP_FS_2 = 0.9974160194396973 +EXP_SPECIFIC_FS_1_0 = 0.09723643958568573 +EXP_SPECIFIC_FS_1_9 = 0.15443961322307587 +EXP_SPECIFIC_FS_1_40 = 0.17233367264270782 +EXP_SPECIFIC_FS_2_15 = 0.26900193095207214 +EXP_SPECIFIC_FS_2_17 = 0.2570513188838959 +EXP_SPECIFIC_FS_2_40 = 0.11656410992145538 +EXP_BARK_0 = 0.5 +EXP_BARK_9 = 5.0 +EXP_BARK_40 = 20.5 +EXP_FREQ_0 = 56.417020507724274 +EXP_FREQ_9 = 498.9473684210526 +EXP_FREQ_40 = 6875.975124656844 + + +@pytest.mark.dependency() +def test_fs_instantiation(dpf_sound_test_server): + fs_computer = FluctuationStrength() + assert fs_computer != None + + +@pytest.mark.dependency(depends=["test_fs_instantiation"]) +def test_fs_process(dpf_sound_test_server): + fs_computer = FluctuationStrength() + + # No signal -> error + with pytest.raises( + PyDpfSoundException, + match="No signal for fluctuation strength computation. Use FluctuationStrength.signal.", + ): + fs_computer.process() + + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal as field container + fs_computer.signal = fc + # Compute: no error + fs_computer.process() + + # Set signal as field + fs_computer.signal = fc[0] + # Compute: no error + fs_computer.process() + + +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_get_output(dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Fluctuation strength not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use FluctuationStrength.process().", + ): + output = fs_computer.get_output() + assert output == None + + # Set signal + fs_computer.signal = fc + + # Compute + fs_computer.process() + + (fs, specific_fs) = fs_computer.get_output() + assert fs != None + assert type(fs) == FieldsContainer + assert specific_fs != None + assert type(specific_fs) == FieldsContainer + + +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_get_fluctuation_strength(dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Fluctuation strength not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use FluctuationStrength.process().", + ): + output = fs_computer.get_fluctuation_strength() + assert output == None + + # Set signal as a field + fs_computer.signal = fc[0] + + # Compute + fs_computer.process() + + # Request second channel's fluctuation strength while signal is a field (mono) -> error + with pytest.raises( + PyDpfSoundException, match="Specified channel index \\(1\\) does not exist." + ): + fs = fs_computer.get_fluctuation_strength(1) + + fs = fs_computer.get_fluctuation_strength(0) + assert type(fs) == np.float64 + assert fs == pytest.approx(EXP_FS_1) + + # Set signal as a fields container + fs_computer.signal = fc + + # Compute + fs_computer.process() + + # Request second channel's fluctuation strength while signal is mono -> error + with pytest.raises( + PyDpfSoundException, match="Specified channel index \\(1\\) does not exist." + ): + fs = fs_computer.get_fluctuation_strength(1) + + fs = fs_computer.get_fluctuation_strength(0) + assert type(fs) == np.float64 + assert fs == pytest.approx(EXP_FS_1) + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_fluctuating_tone_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + fs_computer.process() + + fs = fs_computer.get_fluctuation_strength(1) + assert type(fs) == np.float64 + assert fs == pytest.approx(EXP_FS_2) + + +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_get_specific_fluctuation_strength(dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Fluctuation strength not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use FluctuationStrength.process().", + ): + output = fs_computer.get_specific_fluctuation_strength() + assert output == None + + # Set signal + fs_computer.signal = fc + + # Compute + fs_computer.process() + + specific_fs = fs_computer.get_specific_fluctuation_strength() + + assert type(specific_fs) == np.ndarray + assert len(specific_fs) == 47 + assert specific_fs[0] == pytest.approx(EXP_SPECIFIC_FS_1_0) + assert specific_fs[9] == pytest.approx(EXP_SPECIFIC_FS_1_9) + assert specific_fs[40] == pytest.approx(EXP_SPECIFIC_FS_1_40) + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_fluctuating_tone_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + fs_computer.process() + + specific_fs = fs_computer.get_specific_fluctuation_strength(1) + + assert type(specific_fs) == np.ndarray + assert len(specific_fs) == 47 + assert specific_fs[15] == pytest.approx(EXP_SPECIFIC_FS_2_15) + assert specific_fs[17] == pytest.approx(EXP_SPECIFIC_FS_2_17) + assert specific_fs[40] == pytest.approx(EXP_SPECIFIC_FS_2_40) + + +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_get_bark_band_indexes(dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Fluctuation strength not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use FluctuationStrength.process().", + ): + output = fs_computer.get_bark_band_indexes() + assert output == None + + # Set signal as a fields container + fs_computer.signal = fc + + # Compute + fs_computer.process() + + bark_band_indexes = fs_computer.get_bark_band_indexes() + assert type(bark_band_indexes) == np.ndarray + assert len(bark_band_indexes) == 47 + assert bark_band_indexes[0] == pytest.approx(EXP_BARK_0) + assert bark_band_indexes[9] == pytest.approx(EXP_BARK_9) + assert bark_band_indexes[40] == pytest.approx(EXP_BARK_40) + + # Set signal as a field + fs_computer.signal = fc[0] + + # Compute + fs_computer.process() + + bark_band_indexes = fs_computer.get_bark_band_indexes() + assert type(bark_band_indexes) == np.ndarray + assert len(bark_band_indexes) == 47 + assert bark_band_indexes[0] == pytest.approx(EXP_BARK_0) + assert bark_band_indexes[9] == pytest.approx(EXP_BARK_9) + assert bark_band_indexes[40] == pytest.approx(EXP_BARK_40) + + +@pytest.mark.dependency(depends=["test_fs_get_bark_band_indexes"]) +def test_fs_get_bark_band_frequencies(dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + # assert pytest.data_path_flute_in_container == 'toto' + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + fs_computer.signal = fc + + # Compute + fs_computer.process() + + bark_band_frequencies = fs_computer.get_bark_band_frequencies() + assert type(bark_band_frequencies) == np.ndarray + assert len(bark_band_frequencies) == 47 + assert bark_band_frequencies[0] == pytest.approx(EXP_FREQ_0) + assert bark_band_frequencies[9] == pytest.approx(EXP_FREQ_9) + assert bark_band_frequencies[40] == pytest.approx(EXP_FREQ_40) + + +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_get_output_as_nparray_from_fields_container(dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Fluctuation strength not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use FluctuationStrength.process().", + ): + output = fs_computer.get_output_as_nparray() + assert output == None + + # Set signal + fs_computer.signal = fc + + # Compute + fs_computer.process() + + (fs, specific_fs) = fs_computer.get_output_as_nparray() + + assert type(fs) == np.ndarray + assert len(fs) == 1 + assert fs[0] == pytest.approx(EXP_FS_1) + assert type(specific_fs) == np.ndarray + assert len(specific_fs) == 47 + assert specific_fs[0] == pytest.approx(EXP_SPECIFIC_FS_1_0) + assert specific_fs[9] == pytest.approx(EXP_SPECIFIC_FS_1_9) + assert specific_fs[40] == pytest.approx(EXP_SPECIFIC_FS_1_40) + + +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_get_output_as_nparray_from_field(dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + fs_computer.signal = fc[0] + + # Compute + fs_computer.process() + + (fs, specific_fs) = fs_computer.get_output_as_nparray() + + assert type(fs) == np.ndarray + assert len(fs) == 1 + assert fs[0] == pytest.approx(EXP_FS_1) + assert type(specific_fs) == np.ndarray + assert len(specific_fs) == 47 + assert specific_fs[0] == pytest.approx(EXP_SPECIFIC_FS_1_0) + assert specific_fs[9] == pytest.approx(EXP_SPECIFIC_FS_1_9) + assert specific_fs[40] == pytest.approx(EXP_SPECIFIC_FS_1_40) + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_plot_from_fields_container(mock_show, dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + fs_computer.signal = fc + + # Fluctuation strength not computed yet -> error + with pytest.raises( + PyDpfSoundException, + match="Output has not been processed yet, use FluctuationStrength.process().", + ): + fs_computer.plot() + + # Compute + fs_computer.process() + + # Plot + fs_computer.plot() + + # Add a second signal in the fields container + wav_loader = LoadWav(pytest.data_path_fluctuating_tone_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute + fs_computer.process() + + # Plot + fs_computer.plot() + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_fs_process"]) +def test_fs_plot_from_field(mock_show, dpf_sound_test_server): + fs_computer = FluctuationStrength() + # Get a signal + wav_loader = LoadWav(pytest.data_path_fluctuating_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + fs_computer.signal = fc[0] + + # Compute + fs_computer.process() + + # Plot + fs_computer.plot() + + +@pytest.mark.dependency(depends=["test_fs_instantiation"]) +def test_fs_set_get_signal(dpf_sound_test_server): + fs_computer = FluctuationStrength() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + fs_computer.signal = fc + fc_from_get = fs_computer.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 diff --git a/tests/tests_psychoacoustics/test_psychoacoustics_loudness_iso532_1_stationary.py b/tests/tests_psychoacoustics/test_psychoacoustics_loudness_iso532_1_stationary.py new file mode 100644 index 000000000..678615b1b --- /dev/null +++ b/tests/tests_psychoacoustics/test_psychoacoustics_loudness_iso532_1_stationary.py @@ -0,0 +1,413 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.psychoacoustics import LoudnessISO532_1_Stationary +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav + + +@pytest.mark.dependency() +def test_loudness_iso_532_1_stationary_instantiation(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + assert loudness_computer != None + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_instantiation"]) +def test_loudness_iso_532_1_stationary_process(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + + # No signal -> error + with pytest.raises( + PyDpfSoundException, + match="No signal for loudness computation. Use LoudnessISO532_1_Stationary.signal.", + ): + loudness_computer.process() + + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal as field container + loudness_computer.signal = fc + # Compute: no error + loudness_computer.process() + + # Set signal as field + loudness_computer.signal = fc[0] + # Compute: no error + loudness_computer.process() + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_get_output(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc + + # Loudness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use LoudnessISO532_1_Stationary.process().", + ): + output = loudness_computer.get_output() + assert output == None + + # Compute + loudness_computer.process() + + (loudness_sone, loudness_level_phon, specific_loudness) = loudness_computer.get_output() + assert loudness_sone != None + assert type(loudness_sone) == FieldsContainer + assert loudness_level_phon != None + assert type(loudness_level_phon) == FieldsContainer + assert specific_loudness != None + assert type(specific_loudness) == FieldsContainer + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_get_loudness_sone(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal as a field + loudness_computer.signal = fc[0] + + # Loudness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use LoudnessISO532_1_Stationary.process().", + ): + output = loudness_computer.get_loudness_sone() + assert output == None + + # Compute + loudness_computer.process() + + # Request second channel's loudness while signal is a field (mono) -> error + with pytest.raises( + PyDpfSoundException, match="Specified channel index \\(1\\) does not exist." + ): + loudness_sone = loudness_computer.get_loudness_sone(1) + + loudness_sone = loudness_computer.get_loudness_sone(0) + assert type(loudness_sone) == np.float64 + assert loudness_sone == pytest.approx(39.58000183105469) + + # Set signal as a fields container + loudness_computer.signal = fc + # Compute + loudness_computer.process() + + loudness_sone = loudness_computer.get_loudness_sone(0) + assert type(loudness_sone) == np.float64 + assert loudness_sone == pytest.approx(39.58000183105469) + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_flute2_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + loudness_computer.process() + + loudness_sone = loudness_computer.get_loudness_sone(0) + assert type(loudness_sone) == np.float64 + assert loudness_sone == pytest.approx(39.58000183105469) + loudness_sone = loudness_computer.get_loudness_sone(1) + assert type(loudness_sone) == np.float64 + assert loudness_sone == pytest.approx(16.18000030517578) + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_get_loudness_level_phon(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc + + # Loudness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use LoudnessISO532_1_Stationary.process().", + ): + output = loudness_computer.get_loudness_level_phon() + assert output == None + + # Compute + loudness_computer.process() + + loudness_level_phon = loudness_computer.get_loudness_level_phon() + assert type(loudness_level_phon) == np.float64 + assert loudness_level_phon == pytest.approx(93.0669937133789) + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_get_specific_loudness(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc + + # Loudness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use LoudnessISO532_1_Stationary.process().", + ): + output = loudness_computer.get_specific_loudness() + assert output == None + + # Compute + loudness_computer.process() + + specific_loudness = loudness_computer.get_specific_loudness() + assert type(specific_loudness) == np.ndarray + assert len(specific_loudness) == 240 + assert specific_loudness[0] == pytest.approx(0.0) + assert specific_loudness[9] == pytest.approx(0.15664348006248474) + assert specific_loudness[40] == pytest.approx(1.3235466480255127) + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_flute2_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + loudness_computer.process() + + specific_loudness = loudness_computer.get_specific_loudness(1) + assert type(specific_loudness) == np.ndarray + assert len(specific_loudness) == 240 + assert specific_loudness[0] == pytest.approx(0.0) + assert specific_loudness[9] == pytest.approx(0.008895192295312881) + assert specific_loudness[40] == pytest.approx(0.4043666124343872) + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_get_bark_band_indexes(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal as a fields container + loudness_computer.signal = fc + + # Loudness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use LoudnessISO532_1_Stationary.process().", + ): + output = loudness_computer.get_bark_band_indexes() + assert output == None + + # Compute + loudness_computer.process() + + bark_band_indexes = loudness_computer.get_bark_band_indexes() + assert type(bark_band_indexes) == np.ndarray + assert len(bark_band_indexes) == 240 + assert bark_band_indexes[0] == pytest.approx(0.10000000149011612) + assert bark_band_indexes[9] == pytest.approx(1.0000000149011612) + assert bark_band_indexes[40] == pytest.approx(4.100000061094761) + + # Set signal as a field + loudness_computer.signal = fc[0] + # Compute + loudness_computer.process() + + bark_band_indexes = loudness_computer.get_bark_band_indexes() + assert type(bark_band_indexes) == np.ndarray + assert len(bark_band_indexes) == 240 + assert bark_band_indexes[0] == pytest.approx(0.10000000149011612) + assert bark_band_indexes[9] == pytest.approx(1.0000000149011612) + assert bark_band_indexes[40] == pytest.approx(4.100000061094761) + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_get_bark_band_indexes"]) +def test_loudness_iso_532_1_stationary_get_bark_band_frequencies(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc + # Compute + loudness_computer.process() + + bark_band_frequencies = loudness_computer.get_bark_band_frequencies() + assert type(bark_band_frequencies) == np.ndarray + assert len(bark_band_frequencies) == 240 + assert bark_band_frequencies[0] == pytest.approx(21.33995930840456) + assert bark_band_frequencies[9] == pytest.approx(102.08707043772274) + assert bark_band_frequencies[40] == pytest.approx(400.79351405718324) + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_get_output_as_nparray_from_fields_container( + dpf_sound_test_server, +): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc + + # Loudness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use LoudnessISO532_1_Stationary.process().", + ): + output = loudness_computer.get_output_as_nparray() + assert output == None + + # Compute + loudness_computer.process() + + ( + loudness_sone, + loudness_level_phon, + specific_loudness, + ) = loudness_computer.get_output_as_nparray() + assert type(loudness_sone) == np.ndarray + assert len(loudness_sone) == 1 + assert loudness_sone[0] == pytest.approx(39.58000183105469) + assert type(loudness_level_phon) == np.ndarray + assert len(loudness_level_phon) == 1 + assert loudness_level_phon[0] == pytest.approx(93.0669937133789) + assert type(specific_loudness) == np.ndarray + assert len(specific_loudness) == 240 + assert specific_loudness[0] == pytest.approx(0.0) + assert specific_loudness[9] == pytest.approx(0.15664348006248474) + assert specific_loudness[40] == pytest.approx(1.3235466480255127) + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_get_output_as_nparray_from_field(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc[0] + # Compute + loudness_computer.process() + + ( + loudness_sone, + loudness_level_phon, + specific_loudness, + ) = loudness_computer.get_output_as_nparray() + assert type(loudness_sone) == np.ndarray + assert len(loudness_sone) == 1 + assert loudness_sone[0] == pytest.approx(39.58000183105469) + assert type(loudness_level_phon) == np.ndarray + assert len(loudness_level_phon) == 1 + assert loudness_level_phon[0] == pytest.approx(93.0669937133789) + assert type(specific_loudness) == np.ndarray + assert len(specific_loudness) == 240 + assert specific_loudness[0] == pytest.approx(0.0) + assert specific_loudness[9] == pytest.approx(0.15664348006248474) + assert specific_loudness[40] == pytest.approx(1.3235466480255127) + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_plot_from_fields_container(mock_show, dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc + + # Loudness not computed yet -> error + with pytest.raises( + PyDpfSoundException, + match="Output has not been processed yet, use LoudnessISO532_1_Stationary.process().", + ): + loudness_computer.plot() + + # Compute + loudness_computer.process() + + # Plot + loudness_computer.plot() + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_flute2_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + loudness_computer.process() + + # Plot + loudness_computer.plot() + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_process"]) +def test_loudness_iso_532_1_stationary_plot_from_field(mock_show, dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + # Get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + loudness_computer.signal = fc[0] + # Compute + loudness_computer.process() + + # Plot + loudness_computer.plot() + + +@pytest.mark.dependency(depends=["test_loudness_iso_532_1_stationary_instantiation"]) +def test_loudness_iso_532_1_stationary_set_get_signal(dpf_sound_test_server): + loudness_computer = LoudnessISO532_1_Stationary() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + loudness_computer.signal = fc + fc_from_get = loudness_computer.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 diff --git a/tests/tests_psychoacoustics/test_psychoacoustics_loudness_iso_532_1_time_varying.py b/tests/tests_psychoacoustics/test_psychoacoustics_loudness_iso_532_1_time_varying.py new file mode 100644 index 000000000..0ecef4ff7 --- /dev/null +++ b/tests/tests_psychoacoustics/test_psychoacoustics_loudness_iso_532_1_time_varying.py @@ -0,0 +1,547 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.psychoacoustics import LoudnessISO532_1_TimeVarying +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException +from ansys.dpf.sound.signal_utilities import LoadWav + + +@pytest.mark.dependency() +def test_loudness_532_1_time_varying_instantiation(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + assert time_varying_loudness_computer != None + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_instantiation"]) +def test_loudness_532_1_time_varying_process(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + + # no signal -> error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + time_varying_loudness_computer.process() + assert ( + str(excinfo.value) + == "No signal for loudness vs time computation. Use LoudnessISO532_1_TimeVarying.signal" + ) + + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal as field container + time_varying_loudness_computer.signal = fc + # compute: no error + time_varying_loudness_computer.process() + + # set signal as field + time_varying_loudness_computer.signal = fc[0] + # compute: no error + time_varying_loudness_computer.process() + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_output(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_output() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + ( + loudnessVsTimeSone, + N5, + N10, + loudnessVsTimePhon, + L5, + L10, + ) = time_varying_loudness_computer.get_output() + assert loudnessVsTimeSone != None + assert N5 != None + assert N10 != None + assert loudnessVsTimePhon != None + assert L5 != None + assert L10 != None + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_output_as_nparray_from_field_container( + dpf_sound_test_server, +): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_output_as_nparray() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + ( + loudnessVsTimeSone, + N5, + N10, + loudnessVsTimePhon, + L5, + L10, + ) = time_varying_loudness_computer.get_output_as_nparray() + + assert type(loudnessVsTimeSone) == np.ndarray + assert len(loudnessVsTimeSone) == 1770 + assert loudnessVsTimeSone[0] == pytest.approx(0.0) + assert loudnessVsTimeSone[10] == pytest.approx(0.06577175855636597) + assert loudnessVsTimeSone[100] == pytest.approx(5.100262641906738) + assert type(loudnessVsTimePhon) == np.ndarray + assert len(loudnessVsTimePhon) == 1770 + assert loudnessVsTimePhon[0] == pytest.approx(3.0) + assert loudnessVsTimePhon[10] == pytest.approx(15.430279731750488) + assert loudnessVsTimePhon[100] == pytest.approx(63.505714416503906) + assert type(N5) == np.ndarray + assert len(N5) == 1 + assert N5[0] == pytest.approx(45.12802505493164) + assert type(N10) == np.ndarray + assert len(N10) == 1 + assert N10[0] == pytest.approx(44.12368392944336) + assert type(L5) == np.ndarray + assert len(L5) == 1 + assert L5[0] == pytest.approx(94.95951843261719) + assert type(L10) == np.ndarray + assert len(L10) == 1 + assert L10[0] == pytest.approx(94.63481140136719) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_output_as_nparray_from_field(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + # compute + time_varying_loudness_computer.process() + + ( + loudnessVsTimeSone, + N5, + N10, + loudnessVsTimePhon, + L5, + L10, + ) = time_varying_loudness_computer.get_output_as_nparray() + assert type(loudnessVsTimeSone) == np.ndarray + assert len(loudnessVsTimeSone) == 1770 + assert loudnessVsTimeSone[0] == pytest.approx(0.0) + assert loudnessVsTimeSone[10] == pytest.approx(0.06577175855636597) + assert loudnessVsTimeSone[100] == pytest.approx(5.100262641906738) + assert type(loudnessVsTimePhon) == np.ndarray + assert len(loudnessVsTimePhon) == 1770 + assert loudnessVsTimePhon[0] == pytest.approx(3.0) + assert loudnessVsTimePhon[10] == pytest.approx(15.430279731750488) + assert loudnessVsTimePhon[100] == pytest.approx(63.505714416503906) + assert type(N5) == np.ndarray + assert len(N5) == 1 + assert N5[0] == pytest.approx(45.12802505493164) + assert type(N10) == np.ndarray + assert len(N10) == 1 + assert N10[0] == pytest.approx(44.12368392944336) + assert type(L5) == np.ndarray + assert len(L5) == 1 + assert L5[0] == pytest.approx(94.95951843261719) + assert type(L10) == np.ndarray + assert len(L10) == 1 + assert L10[0] == pytest.approx(94.63481140136719) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_loudness_vs_time_sone(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_loudness_sone_vs_time() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + loudnessVsTimeSone = time_varying_loudness_computer.get_loudness_sone_vs_time(0) + assert type(loudnessVsTimeSone) == np.ndarray + assert len(loudnessVsTimeSone) == 1770 + assert loudnessVsTimeSone[0] == pytest.approx(0.0) + assert loudnessVsTimeSone[10] == pytest.approx(0.06577175855636597) + assert loudnessVsTimeSone[100] == pytest.approx(5.100262641906738) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_loudness_vs_time_sone_from_multichannel_fc( + dpf_sound_test_server, +): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_white_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + loudnessVsTimeSone = time_varying_loudness_computer.get_loudness_sone_vs_time(0) + assert type(loudnessVsTimeSone) == np.ndarray + assert len(loudnessVsTimeSone) == 5000 + assert loudnessVsTimeSone[0] == pytest.approx(0.0) + assert loudnessVsTimeSone[10] == pytest.approx(22.091352462768555) + assert loudnessVsTimeSone[100] == pytest.approx(38.29069900512695) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_loudness_vs_time_phon(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_loudness_level_phon_vs_time() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + loudnessVsTimePhon = time_varying_loudness_computer.get_loudness_level_phon_vs_time(0) + assert type(loudnessVsTimePhon) == np.ndarray + assert len(loudnessVsTimePhon) == 1770 + assert loudnessVsTimePhon[0] == pytest.approx(3.0) + assert loudnessVsTimePhon[10] == pytest.approx(15.430279731750488) + assert loudnessVsTimePhon[100] == pytest.approx(63.505714416503906) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_loudness_vs_time_from_multichannel_fc( + dpf_sound_test_server, +): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_white_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + loudnessVsTimePhon = time_varying_loudness_computer.get_loudness_level_phon_vs_time(0) + assert type(loudnessVsTimePhon) == np.ndarray + assert len(loudnessVsTimePhon) == 5000 + assert loudnessVsTimePhon[0] == pytest.approx(3.0) + assert loudnessVsTimePhon[10] == pytest.approx(84.65409851074219) + assert loudnessVsTimePhon[100] == pytest.approx(92.58921813964844) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_N5(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_N5_sone() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + N5 = time_varying_loudness_computer.get_N5_sone() + assert N5 == pytest.approx(45.12802505493164) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_N5_from_field(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + # compute + time_varying_loudness_computer.process() + + N5 = time_varying_loudness_computer.get_N5_sone() + assert N5 == pytest.approx(45.12802505493164) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_N10(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_N10_sone() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + N10 = time_varying_loudness_computer.get_N10_sone() + assert N10 == pytest.approx(44.12368392944336) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_N10_from_field(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + # compute + time_varying_loudness_computer.process() + + N10 = time_varying_loudness_computer.get_N10_sone() + assert N10 == pytest.approx(44.12368392944336) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_L5(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_L5_phon() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + L5 = time_varying_loudness_computer.get_L5_phon() + assert L5 == pytest.approx(94.95951843261719) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_L5_from_field(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + # compute + time_varying_loudness_computer.process() + + L5 = time_varying_loudness_computer.get_L5_phon() + assert L5 == pytest.approx(94.95951843261719) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_L10(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + output = time_varying_loudness_computer.get_L10_phon() + assert output == None + + # set signal + time_varying_loudness_computer.signal = fc + # compute + time_varying_loudness_computer.process() + + L10 = time_varying_loudness_computer.get_L10_phon() + assert L10 == pytest.approx(94.63481140136719) + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_get_L10_from_field(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + # compute + time_varying_loudness_computer.process() + + L10 = time_varying_loudness_computer.get_L10_phon() + assert L10 == pytest.approx(94.63481140136719) + + +def test_loudness_532_1_time_varying_get_time_scale(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + + assert time_varying_loudness_computer.get_time_scale() == None + + time_varying_loudness_computer.process() + time_scale = time_varying_loudness_computer.get_time_scale() + + assert len(time_scale) == 1770 + assert time_scale[0] == 0 + assert time_scale[10] == pytest.approx(0.019999999552965164) + assert time_scale[42] == pytest.approx(0.08399999886751175) + assert time_scale[100] == pytest.approx(0.20000000298023224) + assert time_scale[110] == pytest.approx(0.2199999988079071) + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_plot_from_field_container(mock_show, dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + + # Load a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + time_varying_loudness_computer.signal = fc + + # Plot before process -> error + with pytest.raises( + PyDpfSoundException, + match="Output has not been processed yet, use LoudnessISO532_1_TimeVarying.process().", + ): + time_varying_loudness_computer.plot() + + # Compute + time_varying_loudness_computer.process() + + # Plot + time_varying_loudness_computer.plot() + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_flute2_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + time_varying_loudness_computer.process() + + # Plot + time_varying_loudness_computer.plot() + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_process"]) +def test_loudness_532_1_time_varying_plot_from_field(mock_show, dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + # compute + time_varying_loudness_computer.process() + + # plot + time_varying_loudness_computer.plot() + + +@pytest.mark.dependency(depends=["test_loudness_532_1_time_varying_instantiation"]) +def test_loudness_532_1_time_varying_set_get_signal(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + time_varying_loudness_computer.signal = fc + fc_from_get = time_varying_loudness_computer.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 + + +def test_loudness_532_1_time_varying_check_channel_index(dpf_sound_test_server): + time_varying_loudness_computer = LoudnessISO532_1_TimeVarying() + # get a signal + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # set signal + time_varying_loudness_computer.signal = fc[0] + + # Nothing computed -> false + assert ( + time_varying_loudness_computer._LoudnessISO532_1_TimeVarying__check_channel_index(0) + == False + ) + + time_varying_loudness_computer.process() + + # check for channel 0 + assert ( + time_varying_loudness_computer._LoudnessISO532_1_TimeVarying__check_channel_index(0) == True + ) + + # check for unexisting channel as Field + with pytest.raises(PyDpfSoundException) as excinfo: + time_varying_loudness_computer._LoudnessISO532_1_TimeVarying__check_channel_index(1) + assert str(excinfo.value) == "Specified channel index (1) does not exist." + + # work with FC + time_varying_loudness_computer.signal = fc + time_varying_loudness_computer.process() + # check for unexisting channel as FC + with pytest.raises(PyDpfSoundException) as excinfo: + time_varying_loudness_computer._LoudnessISO532_1_TimeVarying__check_channel_index(1) + assert str(excinfo.value) == "Specified channel index (1) does not exist." diff --git a/tests/tests_psychoacoustics/test_psychoacoustics_roughness.py b/tests/tests_psychoacoustics/test_psychoacoustics_roughness.py new file mode 100644 index 000000000..c30dccb6a --- /dev/null +++ b/tests/tests_psychoacoustics/test_psychoacoustics_roughness.py @@ -0,0 +1,391 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.psychoacoustics import Roughness +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav + +EXP_ROUGHNESS_1 = 0.5495809316635132 +EXP_ROUGHNESS_2 = 0.20937225222587585 +EXP_SPECIFIC_ROUGHNESS_1_0 = 0.0018477396806702018 +EXP_SPECIFIC_ROUGHNESS_1_9 = 0.0060088788159191618 +EXP_SPECIFIC_ROUGHNESS_1_40 = 0.062388259917497635 +EXP_SPECIFIC_ROUGHNESS_2_15 = 0.03811583295464516 +EXP_SPECIFIC_ROUGHNESS_2_17 = 0.18448638916015625 +EXP_SPECIFIC_ROUGHNESS_2_40 = 0.0 +EXP_BARK_0 = 0.5 +EXP_BARK_9 = 5.0 +EXP_BARK_40 = 20.5 +EXP_FREQ_0 = 56.417020507724274 +EXP_FREQ_9 = 498.9473684210526 +EXP_FREQ_40 = 6875.975124656844 + + +@pytest.mark.dependency() +def test_roughness_instantiation(dpf_sound_test_server): + roughness_computer = Roughness() + assert roughness_computer != None + + +@pytest.mark.dependency(depends=["test_roughness_instantiation"]) +def test_roughness_process(dpf_sound_test_server): + roughness_computer = Roughness() + + # No signal -> error + with pytest.raises( + PyDpfSoundException, + match="No signal for roughness computation. Use Roughness.signal.", + ): + roughness_computer.process() + + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal as field container + roughness_computer.signal = fc + # Compute: no error + roughness_computer.process() + + # Set signal as field + roughness_computer.signal = fc[0] + # Compute: no error + roughness_computer.process() + + +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_get_output(dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Roughness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use Roughness.process().", + ): + output = roughness_computer.get_output() + assert output == None + + # Set signal + roughness_computer.signal = fc + + # Compute + roughness_computer.process() + + (roughness, specific_roughness) = roughness_computer.get_output() + assert roughness != None + assert type(roughness) == FieldsContainer + assert specific_roughness != None + assert type(specific_roughness) == FieldsContainer + + +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_get_roughness(dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Roughness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use Roughness.process().", + ): + output = roughness_computer.get_roughness() + assert output == None + + # Set signal as a field + roughness_computer.signal = fc[0] + + # Compute + roughness_computer.process() + + # Request second channel's roughness while signal is a field (mono) -> error + with pytest.raises( + PyDpfSoundException, match="Specified channel index \\(1\\) does not exist." + ): + roughness = roughness_computer.get_roughness(1) + + roughness = roughness_computer.get_roughness(0) + assert type(roughness) == np.float64 + assert roughness == pytest.approx(EXP_ROUGHNESS_1) + + # Set signal as a fields container + roughness_computer.signal = fc + + # Compute + roughness_computer.process() + + # Request second channel's roughness while signal is mono -> error + with pytest.raises( + PyDpfSoundException, match="Specified channel index \\(1\\) does not exist." + ): + roughness = roughness_computer.get_roughness(1) + + roughness = roughness_computer.get_roughness(0) + assert type(roughness) == np.float64 + assert roughness == pytest.approx(EXP_ROUGHNESS_1) + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_rough_tone_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + roughness_computer.process() + + roughness = roughness_computer.get_roughness(1) + assert type(roughness) == np.float64 + assert roughness == pytest.approx(EXP_ROUGHNESS_2) + + +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_get_specific_roughness(dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Roughness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use Roughness.process().", + ): + output = roughness_computer.get_specific_roughness() + assert output == None + + # Set signal + roughness_computer.signal = fc + + # Compute + roughness_computer.process() + + specific_roughness = roughness_computer.get_specific_roughness() + assert type(specific_roughness) == np.ndarray + assert len(specific_roughness) == 47 + assert specific_roughness[0] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_0) + assert specific_roughness[9] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_9) + assert specific_roughness[40] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_40) + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_rough_tone_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + roughness_computer.process() + + specific_roughness = roughness_computer.get_specific_roughness(1) + assert type(specific_roughness) == np.ndarray + assert len(specific_roughness) == 47 + assert specific_roughness[15] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_2_15) + assert specific_roughness[17] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_2_17) + assert specific_roughness[40] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_2_40) + + +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_get_bark_band_indexes(dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Roughness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use Roughness.process().", + ): + output = roughness_computer.get_bark_band_indexes() + assert output == None + + # Set signal as a fields container + roughness_computer.signal = fc + + # Compute + roughness_computer.process() + + bark_band_indexes = roughness_computer.get_bark_band_indexes() + assert type(bark_band_indexes) == np.ndarray + assert len(bark_band_indexes) == 47 + assert bark_band_indexes[0] == pytest.approx(EXP_BARK_0) + assert bark_band_indexes[9] == pytest.approx(EXP_BARK_9) + assert bark_band_indexes[40] == pytest.approx(EXP_BARK_40) + + # Set signal as a field + roughness_computer.signal = fc[0] + + # Compute + roughness_computer.process() + + bark_band_indexes = roughness_computer.get_bark_band_indexes() + assert type(bark_band_indexes) == np.ndarray + assert len(bark_band_indexes) == 47 + assert bark_band_indexes[0] == pytest.approx(EXP_BARK_0) + assert bark_band_indexes[9] == pytest.approx(EXP_BARK_9) + assert bark_band_indexes[40] == pytest.approx(EXP_BARK_40) + + +@pytest.mark.dependency(depends=["test_roughness_get_bark_band_indexes"]) +def test_roughness_get_bark_band_frequencies(dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + roughness_computer.signal = fc + + # Compute + roughness_computer.process() + + bark_band_frequencies = roughness_computer.get_bark_band_frequencies() + assert type(bark_band_frequencies) == np.ndarray + assert len(bark_band_frequencies) == 47 + assert bark_band_frequencies[0] == pytest.approx(EXP_FREQ_0) + assert bark_band_frequencies[9] == pytest.approx(EXP_FREQ_9) + assert bark_band_frequencies[40] == pytest.approx(EXP_FREQ_40) + + +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_get_output_as_nparray_from_fields_container(dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Roughness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use Roughness.process().", + ): + output = roughness_computer.get_output_as_nparray() + assert output == None + + # Set signal + roughness_computer.signal = fc + + # Compute + roughness_computer.process() + + (roughness, specific_roughness) = roughness_computer.get_output_as_nparray() + assert type(roughness) == np.ndarray + assert len(roughness) == 1 + assert roughness[0] == pytest.approx(EXP_ROUGHNESS_1) + assert type(specific_roughness) == np.ndarray + assert len(specific_roughness) == 47 + assert specific_roughness[0] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_0) + assert specific_roughness[9] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_9) + assert specific_roughness[40] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_40) + + +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_get_output_as_nparray_from_field(dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + roughness_computer.signal = fc[0] + + # Compute + roughness_computer.process() + + (roughness, specific_roughness) = roughness_computer.get_output_as_nparray() + assert type(roughness) == np.ndarray + assert len(roughness) == 1 + assert roughness[0] == pytest.approx(EXP_ROUGHNESS_1) + assert type(specific_roughness) == np.ndarray + assert len(specific_roughness) == 47 + assert specific_roughness[0] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_0) + assert specific_roughness[9] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_9) + assert specific_roughness[40] == pytest.approx(EXP_SPECIFIC_ROUGHNESS_1_40) + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_plot_from_fields_container(mock_show, dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + roughness_computer.signal = fc + + # Roughness not computed yet -> error + with pytest.raises( + PyDpfSoundException, + match="Output has not been processed yet, use Roughness.process().", + ): + roughness_computer.plot() + + # Compute + roughness_computer.process() + + # Plot + roughness_computer.plot() + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_rough_tone_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute + roughness_computer.process() + + # Plot + roughness_computer.plot() + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_roughness_process"]) +def test_roughness_plot_from_field(mock_show, dpf_sound_test_server): + roughness_computer = Roughness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_rough_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + roughness_computer.signal = fc[0] + + # Compute + roughness_computer.process() + + # Plot + roughness_computer.plot() + + +@pytest.mark.dependency(depends=["test_roughness_instantiation"]) +def test_roughness_set_get_signal(dpf_sound_test_server): + roughness_computer = Roughness() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + roughness_computer.signal = fc + fc_from_get = roughness_computer.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 diff --git a/tests/tests_psychoacoustics/test_psychoacoustics_sharpness.py b/tests/tests_psychoacoustics/test_psychoacoustics_sharpness.py new file mode 100644 index 000000000..97d5344de --- /dev/null +++ b/tests/tests_psychoacoustics/test_psychoacoustics_sharpness.py @@ -0,0 +1,186 @@ +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.psychoacoustics import Sharpness +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav + +EXP_SHARPNESS_1 = 1.6609569787979126 +EXP_SHARPNESS_2 = 2.4972000122070312 + + +@pytest.mark.dependency() +def test_sharpness_instantiation(dpf_sound_test_server): + sharpness_computer = Sharpness() + assert sharpness_computer != None + + +@pytest.mark.dependency(depends=["test_sharpness_instantiation"]) +def test_sharpness_process(dpf_sound_test_server): + sharpness_computer = Sharpness() + + # No signal -> error + with pytest.raises( + PyDpfSoundException, + match="No signal for sharpness computation. Use Sharpness.signal.", + ): + sharpness_computer.process() + + # Get a signal + wav_loader = LoadWav(pytest.data_path_sharp_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal as field container + sharpness_computer.signal = fc + + # Compute: no error + sharpness_computer.process() + + # Set signal as field + sharpness_computer.signal = fc[0] + + # Compute: no error + sharpness_computer.process() + + +@pytest.mark.dependency(depends=["test_sharpness_process"]) +def test_sharpness_get_output(dpf_sound_test_server): + sharpness_computer = Sharpness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_sharp_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + sharpness_computer.signal = fc + + # Compute + sharpness_computer.process() + + sharpness = sharpness_computer.get_output() + assert sharpness != None + assert type(sharpness) == FieldsContainer + + +@pytest.mark.dependency(depends=["test_sharpness_process"]) +def test_sharpness_get_sharpness(dpf_sound_test_server): + sharpness_computer = Sharpness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_sharp_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal as a field + sharpness_computer.signal = fc[0] + + # Sharpness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use Sharpness.process().", + ): + sharpness = sharpness_computer.get_sharpness() + assert sharpness == None + + # Compute + sharpness_computer.process() + + # Request second channel's sharpness while signal is a field (mono) -> error + with pytest.raises( + PyDpfSoundException, match="Specified channel index \\(1\\) does not exist." + ): + sharpness = sharpness_computer.get_sharpness(1) + + sharpness = sharpness_computer.get_sharpness(0) + assert type(sharpness) == np.float64 + assert sharpness == pytest.approx(EXP_SHARPNESS_1) + + # Set signal as a fields container + sharpness_computer.signal = fc + + # Compute + sharpness_computer.process() + + sharpness = sharpness_computer.get_sharpness(0) + assert type(sharpness) == np.float64 + assert sharpness == pytest.approx(EXP_SHARPNESS_1) + + # Add a second signal in the fields container + # Note: No need to re-assign the signal property, as fc is simply an alias for it + wav_loader = LoadWav(pytest.data_path_sharper_noise_in_container) + wav_loader.process() + fc.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + # Compute again + sharpness_computer.process() + sharpness = sharpness_computer.get_sharpness(1) + assert type(sharpness) == np.float64 + assert sharpness == pytest.approx(EXP_SHARPNESS_2) + + +@pytest.mark.dependency(depends=["test_sharpness_process"]) +def test_sharpness_get_output_as_nparray_from_fields_container( + dpf_sound_test_server, +): + sharpness_computer = Sharpness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_sharp_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + sharpness_computer.signal = fc + + # Sharpness not calculated yet -> warning + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been processed yet, use Sharpness.process().", + ): + sharpness = sharpness_computer.get_output_as_nparray() + assert sharpness == None + + # Compute + sharpness_computer.process() + + sharpness = sharpness_computer.get_output_as_nparray() + assert type(sharpness) == np.ndarray + assert len(sharpness) == 1 + assert sharpness[0] == pytest.approx(EXP_SHARPNESS_1) + + +@pytest.mark.dependency(depends=["test_sharpness_process"]) +def test_sharpness_get_output_as_nparray_from_field(dpf_sound_test_server): + sharpness_computer = Sharpness() + # Get a signal + wav_loader = LoadWav(pytest.data_path_sharp_noise_in_container) + wav_loader.process() + fc = wav_loader.get_output() + + # Set signal + sharpness_computer.signal = fc[0] + + # Compute + sharpness_computer.process() + + sharpness = sharpness_computer.get_output_as_nparray() + assert type(sharpness) == np.ndarray + assert len(sharpness) == 1 + assert sharpness[0] == pytest.approx(EXP_SHARPNESS_1) + + +@pytest.mark.dependency(depends=["test_sharpness_instantiation"]) +def test_sharpness_set_get_signal(dpf_sound_test_server): + sharpness_computer = Sharpness() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + sharpness_computer.signal = fc + fc_from_get = sharpness_computer.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 diff --git a/tests/tests_signal_utilities/test_signal_utilities_apply_gain.py b/tests/tests_signal_utilities/test_signal_utilities_apply_gain.py new file mode 100644 index 000000000..b8a445512 --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_apply_gain.py @@ -0,0 +1,126 @@ +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import ApplyGain, LoadWav + + +@pytest.mark.dependency() +def test_apply_gain_instantiation(dpf_sound_test_server): + gain_applier = ApplyGain() + assert gain_applier != None + + +@pytest.mark.dependency(depends=["test_apply_gain_instantiation"]) +def test_apply_gain_process(dpf_sound_test_server): + gain_applier = ApplyGain() + wav_loader = LoadWav(pytest.data_path_flute_in_container) + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + gain_applier.process() + assert str(excinfo.value) == "No signal on which to apply gain. Use ApplyGain.set_signal()." + + wav_loader.process() + fc = wav_loader.get_output() + + # Testing input fields container (no error expected) + gain_applier.signal = fc + gain_applier.process() + + # Testing input field (no error expected) + gain_applier.signal = fc[0] + gain_applier.process() + + +@pytest.mark.dependency(depends=["test_apply_gain_process"]) +def test_apply_gain_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + gain_applier = ApplyGain(signal=fc_signal, gain=12.0, gain_in_db=True) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use ApplyGain.process()." + ): + fc_out = gain_applier.get_output() + + gain_applier.process() + fc_out = gain_applier.get_output() + + assert len(fc_out) == 1 + + gain_applier.signal = fc_signal[0] + gain_applier.process() + f_out = gain_applier.get_output() + + assert len(f_out.data) == 156048 + assert f_out.data[1000] == 0.00024298533389810473 + assert f_out.data[3456] == -0.005102692171931267 + assert f_out.data[30000] == 0.29461970925331116 + assert f_out.data[60000] == -0.09051203727722168 + + +@pytest.mark.dependency(depends=["test_apply_gain_process"]) +def test_apply_gain_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + gain_applier = ApplyGain(signal=fc_signal[0], gain=12.0, gain_in_db=True) + gain_applier.process() + out_arr = gain_applier.get_output_as_nparray() + + assert len(out_arr) == 156048 + assert out_arr[1000] == 0.00024298533389810473 + assert out_arr[3456] == -0.005102692171931267 + assert out_arr[30000] == 0.29461970925331116 + assert out_arr[60000] == -0.09051203727722168 + + gain_applier.signal = fc_signal + gain_applier.process() + out_arr = gain_applier.get_output_as_nparray() + + assert len(out_arr) == 156048 + assert out_arr[1000] == 0.00024298533389810473 + assert out_arr[3456] == -0.005102692171931267 + assert out_arr[30000] == 0.29461970925331116 + assert out_arr[60000] == -0.09051203727722168 + + +@pytest.mark.dependency(depends=["test_apply_gain_instantiation"]) +def test_apply_gain_set_get_signal(dpf_sound_test_server): + gain_applier = ApplyGain() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + gain_applier.signal = fc + fc_from_get = gain_applier.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 + + +@pytest.mark.dependency(depends=["test_apply_gain_instantiation"]) +def test_apply_gain_set_get_gain(dpf_sound_test_server): + gain_applier = ApplyGain() + + gain_applier.gain = 1234.0 + assert gain_applier.gain == 1234.0 + + +@pytest.mark.dependency(depends=["test_apply_gain_instantiation"]) +def test_apply_gain_set_get_gain_in_db(dpf_sound_test_server): + gain_applier = ApplyGain() + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + gain_applier.gain_in_db = 1234.0 + assert str(excinfo.value) == "new_gain_in_db must be a boolean value, either True or False." + + gain_applier.gain_in_db = False + assert gain_applier.gain_in_db == False diff --git a/tests/tests_signal_utilities/test_signal_utilities_create_sound_field.py b/tests/tests_signal_utilities/test_signal_utilities_create_sound_field.py new file mode 100644 index 000000000..0158362a9 --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_create_sound_field.py @@ -0,0 +1,89 @@ +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import CreateSoundField + + +@pytest.mark.dependency() +def test_create_sound_field_instantiation(dpf_sound_test_server): + sound_field_creator = CreateSoundField() + assert sound_field_creator != None + + +@pytest.mark.dependency(depends=["test_create_sound_field_instantiation"]) +def test_create_sound_field_process(dpf_sound_test_server): + sound_field_creator = CreateSoundField() + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + sound_field_creator.process() + assert str(excinfo.value) == "No data to use. Use CreateSoundField.set_data()." + + # No error + arr = np.ones(100) + sound_field_creator.data = arr + sound_field_creator.process() + + +@pytest.mark.dependency(depends=["test_create_sound_field_process"]) +def test_create_sound_field_get_output(dpf_sound_test_server): + sound_field_creator = CreateSoundField(data=np.ones(100)) + + with pytest.warns( + PyDpfSoundWarning, + match="Output has not been yet processed, use CreateSoundField.process().", + ): + f_out = sound_field_creator.get_output() + + sound_field_creator.process() + f_out = sound_field_creator.get_output() + + assert len(f_out) == 100 + assert f_out.data[0] == 1.0 + assert f_out.data[50] == 1.0 + assert f_out.data[99] == 1.0 + + +@pytest.mark.dependency(depends=["test_create_sound_field_process"]) +def test_create_sound_field_get_output_as_np_array(dpf_sound_test_server): + sound_field_creator = CreateSoundField(data=np.ones(100)) + sound_field_creator.process() + out_arr = sound_field_creator.get_output_as_nparray() + + assert len(out_arr) == 100 + assert out_arr[0] == 1.0 + assert out_arr[50] == 1.0 + assert out_arr[99] == 1.0 + + +@pytest.mark.dependency(depends=["test_create_sound_field_instantiation"]) +def test_create_sound_field_set_get_data(dpf_sound_test_server): + sound_field_creator = CreateSoundField() + sound_field_creator.data = np.ones(100) + data = sound_field_creator.data + assert len(data) == 100 + assert data[0] == 1.0 + assert data[50] == 1.0 + assert data[99] == 1.0 + + +@pytest.mark.dependency(depends=["test_create_sound_field_instantiation"]) +def test_create_sound_field_set_get_sampling_frequency(dpf_sound_test_server): + sound_field_creator = CreateSoundField() + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + sound_field_creator.sampling_frequency = -1234.0 + assert str(excinfo.value) == "Sampling frequency must be greater than or equal to 0.0." + + sound_field_creator.sampling_frequency = 1234.0 + assert sound_field_creator.sampling_frequency == 1234.0 + + +@pytest.mark.dependency(depends=["test_create_sound_field_instantiation"]) +def test_create_sound_field_set_get_unit(dpf_sound_test_server): + sound_field_creator = CreateSoundField() + + sound_field_creator.unit = "MyUnit" + assert sound_field_creator.unit == "MyUnit" diff --git a/tests/tests_signal_utilities/test_signal_utilities_crop_signal.py b/tests/tests_signal_utilities/test_signal_utilities_crop_signal.py new file mode 100644 index 000000000..7b6fa342d --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_crop_signal.py @@ -0,0 +1,141 @@ +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import CropSignal, LoadWav + + +@pytest.mark.dependency() +def test_crop_signal_instantiation(dpf_sound_test_server): + signal_cropper = CropSignal() + assert signal_cropper != None + + +@pytest.mark.dependency(depends=["test_crop_signal_instantiation"]) +def test_crop_signal_process(dpf_sound_test_server): + signal_cropper = CropSignal(start_time=0.0, end_time=1.0) + wav_loader = LoadWav(pytest.data_path_flute_in_container) + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + signal_cropper.process() + assert str(excinfo.value) == "No signal to crop. Use CropSignal.set_signal()." + + wav_loader.process() + fc = wav_loader.get_output() + + # Testing input fields container (no error expected) + signal_cropper.signal = fc + signal_cropper.process() + + # Testing input field (no error expected) + signal_cropper.signal = fc[0] + signal_cropper.process() + + +@pytest.mark.dependency(depends=["test_crop_signal_process"]) +def test_crop_signal_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + signal_cropper = CropSignal(signal=fc_signal, start_time=0.0, end_time=1.0) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use CropSignal.process()." + ): + fc_out = signal_cropper.get_output() + + signal_cropper.process() + fc_out = signal_cropper.get_output() + + assert len(fc_out) == 1 + + signal_cropper.signal = fc_signal[0] + signal_cropper.process() + f_out = signal_cropper.get_output() + data = f_out.data + # Checking data size and some random samples + assert len(data) == 44101 + assert data[10] == 0.0 + assert data[1000] == 6.103515625e-05 + assert data[10000] == 0.0308837890625 + assert data[44000] == 0.47772216796875 + + +@pytest.mark.dependency(depends=["test_crop_signal_process"]) +def test_crop_signal_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + signal_cropper = CropSignal(signal=fc_signal, start_time=0.0, end_time=1.0) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use CropSignal.process()." + ): + fc_out = signal_cropper.get_output() + + signal_cropper.process() + data = signal_cropper.get_output_as_nparray() + + assert len(data) == 44101 + assert data[10] == 0.0 + assert data[1000] == 6.103515625e-05 + assert data[10000] == 0.0308837890625 + assert data[44000] == 0.47772216796875 + + signal_cropper.signal = fc_signal[0] + signal_cropper.process() + data = signal_cropper.get_output_as_nparray() + # Checking data size and some random samples + assert len(data) == 44101 + assert data[10] == 0.0 + assert data[1000] == 6.103515625e-05 + assert data[10000] == 0.0308837890625 + assert data[44000] == 0.47772216796875 + + +@pytest.mark.dependency(depends=["test_crop_signal_instantiation"]) +def test_crop_signal_set_get_signal(dpf_sound_test_server): + signal_cropper = CropSignal() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + signal_cropper.signal = fc + fc_from_get = signal_cropper.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 + + +@pytest.mark.dependency(depends=["test_crop_signal_instantiation"]) +def test_crop_signal_set_get_start_end_times(dpf_sound_test_server): + signal_cropper = CropSignal() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + signal_cropper.start_time = -12.0 + assert str(excinfo.value) == "Start time must be greater than or equal to 0.0." + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + signal_cropper.end_time = -12.0 + assert str(excinfo.value) == "End time must be greater than or equal to 0.0." + + signal_cropper.start_time = 1.0 + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + signal_cropper.end_time = 0.5 + assert str(excinfo.value) == "End time must be greater than or equal to the start time." + + signal_cropper.end_time = 1234.0 + + start_time = signal_cropper.start_time + end_time = signal_cropper.end_time + assert start_time == 1.0 + assert end_time == 1234.0 diff --git a/tests/tests_signal_utilities/test_signal_utilities_load_wav.py b/tests/tests_signal_utilities/test_signal_utilities_load_wav.py new file mode 100644 index 000000000..c44497cb8 --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_load_wav.py @@ -0,0 +1,94 @@ +from unittest.mock import patch + +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav + + +@pytest.mark.dependency() +def test_load_wav_instantiation(dpf_sound_test_server): + wav_loader = LoadWav() + assert wav_loader != None + + +@pytest.mark.dependency(depends=["test_load_wav_instantiation"]) +def test_load_wav_process(dpf_sound_test_server): + # Should not return an error + wav_loader_good = LoadWav(pytest.data_path_flute_in_container) + wav_loader_good.process() + + # Should return an error + wav_loader_bad = LoadWav() + + with pytest.raises(PyDpfSoundException) as excinfo: + wav_loader_bad.process() + assert str(excinfo.value) == "Path for loading wav file is not specified. Use LoadWav.set_path." + + +@pytest.mark.dependency(depends=["test_load_wav_process"]) +def test_load_wav_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + + # Loading a wav signal using LoadWav class + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use LoadWav.process()." + ): + fc = wav_loader.get_output() + + wav_loader.process() + fc = wav_loader.get_output() + + # Extracting data + data = fc[0].data + + # Checking data size and some random samples + assert len(data) == 156048 + assert data[10] == 0.0 + assert data[1000] == 6.103515625e-05 + assert data[10000] == 0.0308837890625 + assert data[136047] == -0.084686279296875 + + +@pytest.mark.dependency(depends=["test_load_wav_process"]) +def test_load_wav_get_output_as_nparray(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + + # Loading a wav signal using LoadWav + np_arr = wav_loader.get_output_as_nparray() + + # Checking data size and some random samples + assert len(np_arr) == 156048 + assert np_arr[10] == 0.0 + assert np_arr[1000] == 6.103515625e-05 + assert np_arr[10000] == 0.0308837890625 + assert np_arr[136047] == -0.084686279296875 + + # Tests with a stereo signal + wav_loader_stereo = LoadWav(pytest.data_path_white_noise_in_container) + wav_loader_stereo.process() + + # Loading a wav signal using LoadWav + np_arr = wav_loader_stereo.get_output_as_nparray() + + assert np.shape(np_arr) == (2, 480000) + assert np_arr[1][1000] == 0.0169677734375 + assert np_arr[1][10000] == -0.27001953125 + assert np_arr[1][100000] == -0.0509033203125 + + +@pytest.mark.dependency(depends=["test_load_wav_instantiation"]) +def test_load_wav_get_set_path(dpf_sound_test_server): + wav_loader = LoadWav() + wav_loader.path_to_wav = pytest.data_path_flute_in_container + assert wav_loader.path_to_wav == pytest.data_path_flute_in_container + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_load_wav_process"]) +def test_load_wav_plot(mock_show, dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_white_noise_in_container) + wav_loader.process() + wav_loader.plot() diff --git a/tests/tests_signal_utilities/test_signal_utilities_parent.py b/tests/tests_signal_utilities/test_signal_utilities_parent.py new file mode 100644 index 000000000..f7e23848c --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_parent.py @@ -0,0 +1,9 @@ +import pytest + +from ansys.dpf.sound.signal_utilities import SignalUtilitiesParent + + +@pytest.mark.dependency() +def test_signal_utilities_parent_instanciate(): + pydpf_sound = SignalUtilitiesParent() + assert pydpf_sound != None diff --git a/tests/tests_signal_utilities/test_signal_utilities_resample.py b/tests/tests_signal_utilities/test_signal_utilities_resample.py new file mode 100644 index 000000000..5ddae4c7d --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_resample.py @@ -0,0 +1,135 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav, Resample + + +@pytest.mark.dependency() +def test_resample_instantiation(dpf_sound_test_server): + resampler = Resample() + assert resampler != None + + +@pytest.mark.dependency(depends=["test_resample_instantiation"]) +def test_resample_process(dpf_sound_test_server): + resampler = Resample() + wav_loader = LoadWav(pytest.data_path_flute_in_container) + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + resampler.process() + assert str(excinfo.value) == "No signal to resample. Use Resample.set_signal()." + + wav_loader.process() + fc = wav_loader.get_output() + + # Testing input fields container (no error expected) + resampler.signal = fc + resampler.process() + + # Testing input field (no error expected) + resampler.signal = fc[0] + resampler.process() + + +@pytest.mark.dependency(depends=["test_resample_process"]) +def test_resample_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + resampler = Resample(signal=fc_signal, new_sampling_frequency=88100.0) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use Resample.process()." + ): + fc_out = resampler.get_output() + + resampler.process() + fc_out = resampler.get_output() + + assert len(fc_out) == 1 + + resampler.signal = fc_signal[0] + resampler.process() + f_out = resampler.get_output() + + assert len(f_out.data) == 311743 + assert f_out.data[1000] == 2.9065033686492825e-06 + assert f_out.data[3456] == -0.0007385587086901069 + assert f_out.data[30000] == 0.02302781119942665 + assert f_out.data[60000] == -0.4175410866737366 + + +@pytest.mark.dependency(depends=["test_resample_process"]) +def test_resample_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + resampler = Resample(signal=fc_signal[0], new_sampling_frequency=88100.0) + resampler.process() + out_arr = resampler.get_output_as_nparray() + + assert len(out_arr) == 311743 + assert out_arr[1000] == 2.9065033686492825e-06 + assert out_arr[3456] == -0.0007385587086901069 + assert out_arr[30000] == 0.02302781119942665 + assert out_arr[60000] == -0.4175410866737366 + + resampler.signal = fc_signal + resampler.process() + out_arr = resampler.get_output_as_nparray() + + assert len(out_arr) == 311743 + assert out_arr[1000] == 2.9065033686492825e-06 + assert out_arr[3456] == -0.0007385587086901069 + assert out_arr[30000] == 0.02302781119942665 + assert out_arr[60000] == -0.4175410866737366 + + +@pytest.mark.dependency(depends=["test_resample_instantiation"]) +def test_resample_set_get_signal(dpf_sound_test_server): + resampler = Resample() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + resampler.signal = fc + fc_from_get = resampler.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 + + +@pytest.mark.dependency(depends=["test_resample_instantiation"]) +def test_resample_set_get_sampling_frequency(dpf_sound_test_server): + resampler = Resample() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + resampler.new_sampling_frequency = -12.0 + assert str(excinfo.value) == "Sampling frequency must be strictly greater than 0.0." + + resampler.new_sampling_frequency = 1234.0 + assert resampler.new_sampling_frequency == 1234.0 + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_resample_process"]) +def test_resample_plot(mock_show, dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + resampler = Resample(signal=fc_signal, new_sampling_frequency=88100.0) + resampler.process() + resampler.plot() + + resampler.signal = fc_signal[0] + resampler.process() + resampler.plot() diff --git a/tests/tests_signal_utilities/test_signal_utilities_sum_signals.py b/tests/tests_signal_utilities/test_signal_utilities_sum_signals.py new file mode 100644 index 000000000..9e56540d4 --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_sum_signals.py @@ -0,0 +1,92 @@ +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav, SumSignals + + +@pytest.mark.dependency() +def test_sum_signals_instantiation(dpf_sound_test_server): + sum_gain = SumSignals() + assert sum_gain != None + + +@pytest.mark.dependency(depends=["test_sum_signals_instantiation"]) +def test_sum_signals_process(dpf_sound_test_server): + sum_gain = SumSignals() + wav_loader = LoadWav(pytest.data_path_white_noise_in_container) + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + sum_gain.process() + assert str(excinfo.value) == "No signal on which to apply gain. Use SumSignals.set_signal()." + + wav_loader.process() + fc = wav_loader.get_output() + + # Testing input fields container (no error expected) + sum_gain.signals = fc + sum_gain.process() + + +@pytest.mark.dependency(depends=["test_sum_signals_process"]) +def test_sum_signals_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_white_noise_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + sum_gain = SumSignals(signals=fc_signal) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use SumSignals.process()." + ): + fc_out = sum_gain.get_output() + + sum_gain.process() + f_out = sum_gain.get_output() + + assert len(f_out) == 480000 + assert f_out.data[1000] == 0.033935546875 + assert f_out.data[3456] == 0.22674560546875 + assert f_out.data[30000] == -0.72344970703125 + assert f_out.data[60000] == -0.13690185546875 + + +@pytest.mark.dependency(depends=["test_sum_signals_process"]) +def test_sum_signals_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_white_noise_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + sum_gain = SumSignals(signals=fc_signal) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use SumSignals.process()." + ): + fc_out = sum_gain.get_output() + + sum_gain.process() + out = sum_gain.get_output_as_nparray() + + assert len(out) == 480000 + assert out[1000] == 0.033935546875 + assert out[3456] == 0.22674560546875 + assert out[30000] == -0.72344970703125 + assert out[60000] == -0.13690185546875 + + +@pytest.mark.dependency(depends=["test_sum_signals_instantiation"]) +def test_sum_signals_set_get_signals(dpf_sound_test_server): + sum_gain = SumSignals() + + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + sum_gain.signals = fc + fc_from_get = sum_gain.signals + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 diff --git a/tests/tests_signal_utilities/test_signal_utilities_write_wav.py b/tests/tests_signal_utilities/test_signal_utilities_write_wav.py new file mode 100644 index 000000000..7d4047ec4 --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_write_wav.py @@ -0,0 +1,89 @@ +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav, WriteWav + + +@pytest.mark.dependency() +def test_write_wav_instantiation(dpf_sound_test_server): + wav_writer = WriteWav() + assert wav_writer != None + + +@pytest.mark.dependency(depends=["test_write_wav_instantiation"]) +def test_write_wav_process(dpf_sound_test_server): + wav_writer = WriteWav() + wav_loader = LoadWav(pytest.data_path_flute_in_container) + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + wav_writer.process() + assert str(excinfo.value) == "Path for write wav file is not specified. Use WriteWav.set_path." + + wav_writer.path_to_write = r"C:\data\flute_modified.wav" + + # Error 2 + with pytest.raises(PyDpfSoundException) as excinfo: + wav_writer.process() + assert str(excinfo.value) == "No signal is specified for writing, use WriteWav.set_signal." + + wav_loader.process() + wav_writer.signal = wav_loader.get_output() + + # Computing, no error expected + wav_writer.process() + + +@pytest.mark.dependency(depends=["test_write_wav_instantiation"]) +def test_write_wav_set_get_path(dpf_sound_test_server): + wav_writer = WriteWav() + + wav_writer.path_to_write = r"C:\test\path" + p = wav_writer.path_to_write + + assert p == r"C:\test\path" + + +@pytest.mark.dependency(depends=["test_write_wav_instantiation"]) +def test_write_wav_set_get_bit_depth(dpf_sound_test_server): + wav_writer = WriteWav() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + wav_writer.bit_depth = "int128" + assert ( + str(excinfo.value) + == "Invalid bit depth, accepted values are 'float32', 'int32', 'int16', 'int8'." + ) + + wav_writer.bit_depth = r"int8" + b = wav_writer.bit_depth + + assert b == "int8" + + +@pytest.mark.dependency(depends=["test_write_wav_instantiation"]) +def test_write_wav_set_get_signal(dpf_sound_test_server): + wav_writer = WriteWav() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + wav_writer.signal = fc + fc_from_get = wav_writer.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 + + +@pytest.mark.dependency(depends=["test_write_wav_instantiation"]) +def test_write_wav_plot(dpf_sound_test_server): + wav_writer = WriteWav() + + with pytest.warns(PyDpfSoundWarning, match="Nothing to plot."): + wav_writer.plot() diff --git a/tests/tests_signal_utilities/test_signal_utilities_zero_pad.py b/tests/tests_signal_utilities/test_signal_utilities_zero_pad.py new file mode 100644 index 000000000..e7b27361a --- /dev/null +++ b/tests/tests_signal_utilities/test_signal_utilities_zero_pad.py @@ -0,0 +1,123 @@ +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav, ZeroPad + + +@pytest.mark.dependency() +def test_zero_pad_instantiation(dpf_sound_test_server): + zero_pad = ZeroPad() + assert zero_pad != None + + +@pytest.mark.dependency(depends=["test_zero_pad_instantiation"]) +def test_zero_pad_process(dpf_sound_test_server): + zero_pad = ZeroPad() + wav_loader = LoadWav(pytest.data_path_flute_in_container) + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + zero_pad.process() + assert str(excinfo.value) == "No signal to zero-pad. Use ZeroPad.set_signal()." + + wav_loader.process() + fc = wav_loader.get_output() + + # Testing input fields container (no error expected) + zero_pad.signal = fc + zero_pad.process() + + # Testing input field (no error expected) + zero_pad.signal = fc[0] + zero_pad.process() + + +@pytest.mark.dependency(depends=["test_zero_pad_process"]) +def test_zero_pad_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + zero_pad = ZeroPad(signal=fc_signal, duration_zeros=12.0) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use ZeroPad.process()." + ): + fc_out = zero_pad.get_output() + + zero_pad.process() + fc_out = zero_pad.get_output() + + assert len(fc_out) == 1 + + zero_pad.signal = fc_signal[0] + zero_pad.process() + f_out = zero_pad.get_output() + + assert len(f_out.data) == 685248 + assert f_out.data[1000] == 6.103515625e-05 + assert f_out.data[3456] == -0.00128173828125 + assert f_out.data[30000] == 0.074005126953125 + assert f_out.data[60000] == -0.022735595703125 + assert f_out.data[156048] == 0.0 + assert f_out.data[600000] == 0.0 + + +@pytest.mark.dependency(depends=["test_zero_pad_process"]) +def test_zero_pad_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + zero_pad = ZeroPad(signal=fc_signal[0], duration_zeros=12.0) + zero_pad.process() + out_arr = zero_pad.get_output_as_nparray() + + assert len(out_arr) == 685248 + assert out_arr[1000] == 6.103515625e-05 + assert out_arr[3456] == -0.00128173828125 + assert out_arr[30000] == 0.074005126953125 + assert out_arr[60000] == -0.022735595703125 + assert out_arr[156048] == 0.0 + assert out_arr[600000] == 0.0 + + zero_pad.signal = fc_signal + zero_pad.process() + out_arr = zero_pad.get_output_as_nparray() + + assert len(out_arr) == 685248 + assert out_arr[1000] == 6.103515625e-05 + assert out_arr[3456] == -0.00128173828125 + assert out_arr[30000] == 0.074005126953125 + assert out_arr[60000] == -0.022735595703125 + assert out_arr[156048] == 0.0 + assert out_arr[600000] == 0.0 + + +@pytest.mark.dependency(depends=["test_zero_pad_instantiation"]) +def test_zero_pad_set_get_signal(dpf_sound_test_server): + zero_pad = ZeroPad() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + zero_pad.signal = fc + fc_from_get = zero_pad.signal + + assert fc_from_get.name == "testField" + assert len(fc_from_get) == 1 + assert fc_from_get[0].data[0, 2] == 42 + + +@pytest.mark.dependency(depends=["test_zero_pad_process"]) +def test_zero_pad_set_get_duration_zeros(dpf_sound_test_server): + zero_pad = ZeroPad() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + zero_pad.duration_zeros = -12.0 + assert str(excinfo.value) == "Zero duration must be strictly greater than 0.0." + zero_pad.duration_zeros = 1234.0 + assert zero_pad.duration_zeros == 1234.0 diff --git a/tests/tests_spectrogram_processing/test_spectrogram_processing_isolate_orders.py b/tests/tests_spectrogram_processing/test_spectrogram_processing_isolate_orders.py new file mode 100644 index 000000000..143c7effd --- /dev/null +++ b/tests/tests_spectrogram_processing/test_spectrogram_processing_isolate_orders.py @@ -0,0 +1,234 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav +from ansys.dpf.sound.spectrogram_processing import IsolateOrders + + +@pytest.mark.dependency() +def test_isolate_orders_instantiation(dpf_sound_test_server): + isolate_orders = IsolateOrders() + assert isolate_orders != None + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_process(dpf_sound_test_server): + isolate_orders = IsolateOrders() + wav_loader = LoadWav(pytest.data_path_accel_with_rpm_in_container) + wav_loader.process() + + fc = wav_loader.get_output() + signal = fc[0] + rpm_profile = fc[1] + rpm_profile.time_freq_support = signal.time_freq_support + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + isolate_orders.process() + assert str(excinfo.value) == "No signal for order isolation. Use IsolateOrder.signal." + + isolate_orders.signal = signal + + # Error 2 + with pytest.raises(PyDpfSoundException) as excinfo: + isolate_orders.process() + assert str(excinfo.value) == "No RPM profile for order isolation. Use IsolateOrder.rpm_profile." + + # Testing input fields container (no error expected) + isolate_orders.rpm_profile = rpm_profile + + # Error 3 + with pytest.raises(PyDpfSoundException) as excinfo: + isolate_orders.process() + assert str(excinfo.value) == "No orders for order isolation. Use IsolateOrder.orders." + + isolate_orders.orders = [2, 4] + + try: + isolate_orders.process() + except: + # Should not fail + assert False + + +@pytest.mark.dependency(depends=["test_isolate_orders_process"]) +def test_isolate_orders_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_accel_with_rpm_in_container) + wav_loader.process() + fc = wav_loader.get_output() + signal = fc[0] + rpm_profile = fc[1] + rpm_profile.time_freq_support = signal.time_freq_support + isolate_orders = IsolateOrders(signal=signal, rpm_profile=rpm_profile, orders=[2, 4]) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use IsolateOrders.process()." + ): + fc_out = isolate_orders.get_output() + + isolate_orders.process() + fc_out = isolate_orders.get_output() + + assert len(fc_out) == 909956 + + fc_bis = FieldsContainer() + fc_bis.add_label("channel_number") + fc_bis.add_field({"channel_number": 0}, signal) + isolate_orders.signal = fc_bis + + isolate_orders.process() + fc_out = isolate_orders.get_output() + + assert len(fc_out) == 1 + assert len(fc_out[0].data) == 909956 + + +@pytest.mark.dependency(depends=["test_isolate_orders_process"]) +def test_isolate_orders_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_accel_with_rpm_in_container) + wav_loader.process() + fc = wav_loader.get_output() + signal = fc[0] + rpm_profile = fc[1] + rpm_profile.time_freq_support = signal.time_freq_support + isolate_orders = IsolateOrders(signal=signal, rpm_profile=rpm_profile, orders=[2, 4]) + + isolate_orders.process() + arr = isolate_orders.get_output_as_nparray() + + assert arr[100] == -0.11279940605163574 + assert arr[1000] == -0.016805129125714302 + assert arr[10000] == -0.04283715412020683 + + fc_bis = FieldsContainer() + fc_bis.add_label("channel_number") + fc_bis.add_field({"channel_number": 0}, signal) + isolate_orders.signal = fc_bis + + isolate_orders.process() + arr = isolate_orders.get_output_as_nparray() + + assert arr[100] == -0.11279940605163574 + assert arr[1000] == -0.016805129125714302 + assert arr[10000] == -0.04283715412020683 + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_set_get_signal(dpf_sound_test_server): + isolate_orders = IsolateOrders() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + isolate_orders.signal = fc + f = isolate_orders.signal + + assert len(f[0]) == 3 + assert f[0].data[0, 2] == 42 + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_set_get_fft_size(dpf_sound_test_server): + isolate_orders = IsolateOrders() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + isolate_orders.fft_size = -12.0 + assert str(excinfo.value) == "FFT size must be greater than 0.0." + + isolate_orders.fft_size = 1234.0 + assert isolate_orders.fft_size == 1234.0 + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_set_get_window_overlap(dpf_sound_test_server): + isolate_orders = IsolateOrders() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + isolate_orders.window_overlap = -12.0 + assert str(excinfo.value) == "Window overlap must be between 0.0 and 1.0." + + isolate_orders.window_overlap = 0.5 + assert isolate_orders.window_overlap == 0.5 + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_set_get_window_type(dpf_sound_test_server): + isolate_orders = IsolateOrders() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + isolate_orders.window_type = "InvalidWindow" + assert ( + str(excinfo.value) + == "Invalid window type, accepted values are 'HANNING', 'BLACKMANHARRIS', 'HANN', \ + 'BLACKMAN','HAMMING', 'KAISER', 'BARTLETT', 'RECTANGULAR'." + ) + + isolate_orders.window_type = "KAISER" + assert isolate_orders.window_type == "KAISER" + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_set_get_rpm_profile(dpf_sound_test_server): + isolate_orders = IsolateOrders() + + rpm = Field() + rpm.append([1, 23, 45], 1) + + isolate_orders.rpm_profile = rpm + assert isolate_orders.rpm_profile.data[0, 2] == 45 + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_set_get_orders(dpf_sound_test_server): + isolate_orders = IsolateOrders() + orders = Field() + orders.append([1, 2, 45], 1) + + isolate_orders.orders = orders + assert isolate_orders.orders.data[0, 2] == 45 + + isolate_orders.orders = [1, 2, 45] + assert isolate_orders.orders.data[0, 2] == 45 + + +@pytest.mark.dependency(depends=["test_isolate_orders_instantiation"]) +def test_isolate_orders_set_get_width_selection(dpf_sound_test_server): + isolate_orders = IsolateOrders() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + isolate_orders.width_selection = -12.0 + assert str(excinfo.value) == "Width selection must be greater than 0.0." + + isolate_orders.window_overlap = 0.5 + assert isolate_orders.window_overlap == 0.5 + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_isolate_orders_process"]) +def test_isolate_orders_plot(mock_show, dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_accel_with_rpm_in_container) + wav_loader.process() + fc = wav_loader.get_output() + signal = fc[0] + rpm_profile = fc[1] + rpm_profile.time_freq_support = signal.time_freq_support + isolate_orders = IsolateOrders(signal=signal, rpm_profile=rpm_profile, orders=[2, 4]) + isolate_orders.process() + isolate_orders.plot() + + fc_bis = FieldsContainer() + fc_bis.add_label("channel_number") + fc_bis.add_field({"channel_number": 0}, signal) + isolate_orders.signal = fc_bis + isolate_orders.process() + isolate_orders.plot() diff --git a/tests/tests_spectrogram_processing/test_spectrogram_processing_istft.py b/tests/tests_spectrogram_processing/test_spectrogram_processing_istft.py new file mode 100644 index 000000000..5096bd7d1 --- /dev/null +++ b/tests/tests_spectrogram_processing/test_spectrogram_processing_istft.py @@ -0,0 +1,124 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav +from ansys.dpf.sound.spectrogram_processing import Istft, Stft + + +@pytest.mark.dependency() +def test_istft_instantiation(dpf_sound_test_server): + stft = Istft() + assert stft != None + + +@pytest.mark.dependency(depends=["test_istft_instantiation"]) +def test_istft_process(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + stft = Stft(signal=wav_loader.get_output()) + stft.process() + istft = Istft() + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + istft.process() + assert str(excinfo.value) == "No STFT input for ISTFT computation. Use Istft.stft." + + # Testing input fields container (no error expected) + istft.stft = stft.get_output() + try: + istft.process() + except: + # Should not fail + assert False + + +@pytest.mark.dependency(depends=["test_istft_process"]) +def test_istft_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + stft = Stft(signal=wav_loader.get_output()) + stft.process() + istft = Istft(stft=stft.get_output()) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use Istft.process()." + ): + fc_out = istft.get_output() + + istft.process() + f_out = istft.get_output() + + assert len(f_out) == 156048 + assert f_out.data[100] == -3.790271482090324e-12 + assert f_out.data[2000] == -0.0014953609788790345 + assert f_out.data[30000] == 0.0740051195025444 + + +@pytest.mark.dependency(depends=["test_istft_process"]) +def test_istft_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + stft = Stft(signal=wav_loader.get_output()) + stft.process() + istft = Istft(stft=stft.get_output()) + + istft.process() + arr = istft.get_output_as_nparray() + + assert len(arr) == 156048 + assert arr[100] == -3.790271482090324e-12 + assert arr[2000] == -0.0014953609788790345 + assert arr[30000] == 0.0740051195025444 + + +@pytest.mark.dependency(depends=["test_istft_instantiation"]) +def test_istft_set_get_signal(dpf_sound_test_server): + # Test 1 with error + istft = Istft() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + istft.stft = 456 + assert str(excinfo.value) == "Input must be a Fields container." + + with pytest.raises(PyDpfSoundException) as excinfo: + istft.stft = fc + assert ( + str(excinfo.value) + == "STFT is in the wrong format, make sure it has been computed with the Stft class." + ) + + # Test 2 - No Error + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + stft = Stft(signal=wav_loader.get_output()) + stft.process() + istft.stft = stft.get_output() + + out_stft = istft.stft + + assert out_stft.has_label("time") + assert out_stft.has_label("channel_number") + assert out_stft.has_label("complex") + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_istft_process"]) +def test_istft_plot(mock_show, dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + stft = Stft(signal=wav_loader.get_output()) + stft.process() + istft = Istft(stft=stft.get_output()) + istft.process() + istft.plot() diff --git a/tests/tests_spectrogram_processing/test_spectrogram_processing_parent.py b/tests/tests_spectrogram_processing/test_spectrogram_processing_parent.py new file mode 100644 index 000000000..cb50f813b --- /dev/null +++ b/tests/tests_spectrogram_processing/test_spectrogram_processing_parent.py @@ -0,0 +1,9 @@ +import pytest + +from ansys.dpf.sound.spectrogram_processing import SpectrogramProcessingParent + + +@pytest.mark.dependency() +def test_spectrogram_processing_parent_instanciate(): + pydpf_sound = SpectrogramProcessingParent() + assert pydpf_sound != None diff --git a/tests/tests_spectrogram_processing/test_spectrogram_processing_stft.py b/tests/tests_spectrogram_processing/test_spectrogram_processing_stft.py new file mode 100644 index 000000000..ce711afd8 --- /dev/null +++ b/tests/tests_spectrogram_processing/test_spectrogram_processing_stft.py @@ -0,0 +1,160 @@ +from unittest.mock import patch + +from ansys.dpf.core import Field, FieldsContainer +import numpy as np +import pytest + +from ansys.dpf.sound.pydpf_sound import PyDpfSoundException, PyDpfSoundWarning +from ansys.dpf.sound.signal_utilities import LoadWav +from ansys.dpf.sound.spectrogram_processing import Stft + + +@pytest.mark.dependency() +def test_stft_instantiation(dpf_sound_test_server): + stft = Stft() + assert stft != None + + +@pytest.mark.dependency(depends=["test_stft_instantiation"]) +def test_stft_process(dpf_sound_test_server): + stft = Stft() + wav_loader = LoadWav(pytest.data_path_flute_in_container) + + # Error 1 + with pytest.raises(PyDpfSoundException) as excinfo: + stft.process() + assert str(excinfo.value) == "No signal for STFT. Use Stft.signal." + + wav_loader.process() + fc = wav_loader.get_output() + + # Testing input fields container (no error expected) + stft.signal = fc + try: + stft.process() + except: + # Should not fail + assert False + + +@pytest.mark.dependency(depends=["test_stft_process"]) +def test_stft_get_output(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + stft = Stft(signal=fc_signal) + + with pytest.warns( + PyDpfSoundWarning, match="Output has not been yet processed, use Stft.process()." + ): + fc_out = stft.get_output() + + stft.process() + fc_out = stft.get_output() + + assert len(fc_out) == 310 + assert len(fc_out[100].data) == stft.fft_size + assert fc_out[100].data[0] == -0.11434437334537506 + assert fc_out[200].data[0] == -0.09117653965950012 + assert fc_out[300].data[0] == -0.019828863441944122 + + +@pytest.mark.dependency(depends=["test_stft_process"]) +def test_stft_get_output_as_np_array(dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + stft = Stft(signal=fc_signal) + + stft.process() + arr = stft.get_output_as_nparray() + + assert np.shape(arr) == (stft.fft_size, 155) + + assert type(arr[100, 0]) == np.complex128 + assert arr[100, 50] == (-1.0736324787139893 - 1.4027032852172852j) + assert arr[200, 50] == (0.511505126953125 + 0.3143689036369324j) + assert arr[300, 50] == (-0.03049434721469879 - 0.49174121022224426j) + + +@pytest.mark.dependency(depends=["test_stft_instantiation"]) +def test_stft_set_get_signal(dpf_sound_test_server): + stft = Stft() + fc = FieldsContainer() + fc.labels = ["channel"] + f = Field() + f.data = 42 * np.ones(3) + fc.add_field({"channel": 0}, f) + fc.name = "testField" + stft.signal = fc + f = stft.signal + + assert len(f) == 3 + assert f.data[0, 2] == 42 + + stft.signal = fc[0] + fc_from_get = stft.signal + + assert len(f) == 3 + assert f.data[0, 2] == 42 + + fc.add_field({"channel": 1}, fc[0]) + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + stft.signal = fc + assert str(excinfo.value) == "Input as FieldsContainer can only have one Field (mono signal)." + + +@pytest.mark.dependency(depends=["test_stft_instantiation"]) +def test_stft_set_get_fft_size(dpf_sound_test_server): + stft = Stft() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + stft.fft_size = -12.0 + assert str(excinfo.value) == "FFT size must be greater than 0.0." + + stft.fft_size = 1234.0 + assert stft.fft_size == 1234.0 + + +@pytest.mark.dependency(depends=["test_stft_instantiation"]) +def test_stft_set_get_window_overlap(dpf_sound_test_server): + stft = Stft() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + stft.window_overlap = -12.0 + assert str(excinfo.value) == "Window overlap must be between 0.0 and 1.0." + + stft.window_overlap = 0.5 + assert stft.window_overlap == 0.5 + + +@pytest.mark.dependency(depends=["test_stft_instantiation"]) +def test_stft_set_get_window_type(dpf_sound_test_server): + stft = Stft() + + # Error + with pytest.raises(PyDpfSoundException) as excinfo: + stft.window_type = "InvalidWindow" + assert ( + str(excinfo.value) + == "Invalid window type, accepted values are 'HANNING', 'BLACKMANHARRIS', 'HANN', \ + 'BLACKMAN','HAMMING', 'KAISER', 'BARTLETT', 'RECTANGULAR'." + ) + + stft.window_type = "KAISER" + assert stft.window_type == "KAISER" + + +@patch("matplotlib.pyplot.show") +@pytest.mark.dependency(depends=["test_stft_process"]) +def test_stft_plot(mock_show, dpf_sound_test_server): + wav_loader = LoadWav(pytest.data_path_flute_in_container) + wav_loader.process() + fc_signal = wav_loader.get_output() + stft = Stft(signal=fc_signal) + stft.process() + stft.plot() diff --git a/version/0.1/.buildinfo b/version/0.1/.buildinfo new file mode 100644 index 000000000..28aa90bce --- /dev/null +++ b/version/0.1/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: dbdd396a6eae4ce41d484afa3b0b0182 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/version/0.1/_downloads/076326beb951b431d70d3622e9ee9e3a/005_xtract_feature.ipynb b/version/0.1/_downloads/076326beb951b431d70d3622e9ee9e3a/005_xtract_feature.ipynb new file mode 100644 index 000000000..d34418464 --- /dev/null +++ b/version/0.1/_downloads/076326beb951b431d70d3622e9ee9e3a/005_xtract_feature.ipynb @@ -0,0 +1,212 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Use the Xtract feature {#xtract_feature_example}\r\n\r\nThis example shows how to use the Xtract feature in PyAnsys Sound. It\r\ndemonstrates different capabilities of this feature, such as noise\r\nextraction, tonal extraction, and transient extraction.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Maximum frequency for STFT plots, change according to your need\nMAX_FREQUENCY_PLOT_STFT = 5000.0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set up analysis\r\n\r\nSetting up the analysis consists of loading Ansys libraries, connecting\r\nto the DPF server, and retrieving the example files.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load Ansys libraries.\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom ansys.sound.core.examples_helpers import (\n download_xtract_demo_signal_1_wav,\n download_xtract_demo_signal_2_wav,\n)\nfrom ansys.sound.core.server_helpers import connect_to_or_start_server\nfrom ansys.sound.core.signal_utilities import CropSignal, LoadWav\nfrom ansys.sound.core.spectrogram_processing import Stft\nfrom ansys.sound.core.xtract import (\n Xtract,\n XtractDenoiser,\n XtractDenoiserParameters,\n XtractTonal,\n XtractTonalParameters,\n XtractTransient,\n XtractTransientParameters,\n)\n\n# Connect to a remote server or start a local server\nmy_server = connect_to_or_start_server(use_license_context=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Define a custom function for STFT plots\r\n\r\nDefine a custom function for STFT plots lets you have more control over\r\nwhat you are displaying. While you could use the `Stft.plot()` method,\r\nthe custom function defined here restricts the frequency range of the\r\nplot.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def plot_stft(stft_class, SPLmax, title=\"STFT\", maximum_frequency=MAX_FREQUENCY_PLOT_STFT):\n \"\"\"Plot a short-term Fourier transform (STFT) into a figure window.\n\n Parameters\n ----------\n stft_class: Stft\n Object containing the STFT.\n SPLmax: float\n Maximum value (here in dB SPL) for the colormap.\n title: str\n Title of the figure.\n maximum_frequency: float\n Maximum frequency in Hz to display.\n \"\"\"\n out = stft_class.get_output_as_nparray()\n\n # Extract first half of the STFT (second half is symmetrical)\n half_nfft = int(out.shape[0] / 2) + 1\n magnitude = stft_class.get_stft_magnitude_as_nparray()\n\n # Voluntarily ignore a numpy warning\n np.seterr(divide=\"ignore\")\n magnitude = 20 * np.log10(magnitude[0:half_nfft, :])\n np.seterr(divide=\"warn\")\n\n # Obtain sampling frequency, time steps, and number of time samples\n fs = 1.0 / (\n stft_class.signal.time_freq_support.time_frequencies.data[1]\n - stft_class.signal.time_freq_support.time_frequencies.data[0]\n )\n time_step = np.floor(stft_class.fft_size * (1.0 - stft_class.window_overlap) + 0.5) / fs\n num_time_index = len(stft_class.get_output().get_available_ids_for_label(\"time\"))\n\n # Define boundaries of the plot\n extent = [0, time_step * num_time_index, 0.0, fs / 2.0]\n\n # Plot\n plt.figure()\n plt.imshow(\n magnitude,\n origin=\"lower\",\n aspect=\"auto\",\n cmap=\"jet\",\n extent=extent,\n vmax=SPLmax,\n vmin=(SPLmax - 70.0),\n )\n plt.colorbar(label=\"Magnitude (dB SPL)\")\n plt.ylabel(\"Frequency (Hz)\")\n plt.xlabel(\"Time (s)\")\n plt.ylim([0.0, maximum_frequency]) # Change the value of MAX_FREQUENCY_PLOT_STFT if needed\n plt.title(title)\n plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load a demo signal for Xtract\r\n\r\nLoad a demo signal from a WAV file using the `LoadWav` class. The WAV\r\nfile contains harmonics and shocks.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Return the input data of the example file\npath_xtract_demo_signal_1 = download_xtract_demo_signal_1_wav()\n\n# Load the WAV file\nwav_loader = LoadWav(path_to_wav=path_xtract_demo_signal_1)\nwav_loader.process()\n\n# Plot the signal in time domain\ntime_domain_signal = wav_loader.get_output()[0]\ntime_vector = time_domain_signal.time_freq_support.time_frequencies.data\nplt.plot(time_vector, time_domain_signal.data)\nplt.title(\"Xtract Demo Signal 1\")\nplt.grid(True)\nplt.xlabel(\"Time (s)\")\nplt.ylabel(\"Amplitude (Pa)\")\nplt.show()\n\n# Compute the spectrogram of the signal and plot it\nstft_original = Stft(signal=wav_loader.get_output()[0], fft_size=1024, window_overlap=0.9)\nstft_original.process()\nmax_stft = 20 * np.log10(np.max(stft_original.get_stft_magnitude_as_nparray()))\n\nplot_stft(stft_original, SPLmax=max_stft, maximum_frequency=20000.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Use individual extraction features\r\n\r\nThe following topics show how to use different capabilities of Xtract\r\nindependently.\r\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Noise extraction\r\n\r\nThe goal is to isolate a fan noise deprived of any tonal content in the\r\ndemo signal.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Create a noise pattern using the first two seconds of the signal.\n# First crop the first two seconds of the signal.\nsignal_cropper = CropSignal(signal=time_domain_signal, start_time=0.0, end_time=2.0)\nsignal_cropper.process()\ncropped_signal = signal_cropper.get_output()\n\n# Then use the 'XtractDenoiserParameters' class to create the noise pattern.\nxtract_denoiser_params = XtractDenoiserParameters()\nxtract_denoiser_params.noise_psd = xtract_denoiser_params.create_noise_psd_from_noise_samples(\n signal=cropped_signal, sampling_frequency=44100.0, window_length=100\n)\n\n# Denoise the signal using the 'XtractDenoiser' class.\nxtract_denoiser = XtractDenoiser(\n input_signal=time_domain_signal, input_parameters=xtract_denoiser_params\n)\nxtract_denoiser.process()\n\nnoise_signal = xtract_denoiser.get_output()[1]\n\n# Plot the original signal and the noise signal in the same window\nplt.plot(time_vector, time_domain_signal.data, label=\"Original Signal\")\nplt.plot(time_vector, noise_signal.data, label=\"Noise Signal\")\nplt.grid(True)\nplt.xlabel(\"Time (s)\")\nplt.ylabel(\"Amplitude (Pa)\")\nplt.title(\"Original Signal and Noise Signal\")\nplt.legend()\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tone extraction\r\n\r\nThe goal is to isolate the tones using the right settings.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Try a first attempt of tone extraction with a\n# First set of parameters using the 'XtractTonalParameters' class.\nxtract_tonal_params = XtractTonalParameters()\nxtract_tonal_params.regularity = 1.0\nxtract_tonal_params.maximum_slope = 1000.0\nxtract_tonal_params.minimum_duration = 0.22\nxtract_tonal_params.intertonal_gap = 10.0\nxtract_tonal_params.local_emergence = 2.0\nxtract_tonal_params.fft_size = 2048\n\n## Now perform the tonal extraction using the 'XtractTonal' class\nxtract_tonal = XtractTonal(input_signal=time_domain_signal, input_parameters=xtract_tonal_params)\nxtract_tonal.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the spectrogram to assess the quality of the output.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "stft_modified_signal = Stft(signal=xtract_tonal.get_output()[0], fft_size=1024, window_overlap=0.9)\nstft_modified_signal.process()\n\nprint(\"Plot of the spectrograms with tonal extraction parameters that do not work.\")\n\n## Spectrogram of the original signal\nplot_stft(stft_original, SPLmax=max_stft, title=\"Original Signal\")\n\n## Spectrogram of the modified signal\nplot_stft(stft_modified_signal, SPLmax=max_stft, title=\"Extracted Tones\")\n# You can see from the obtained plot that the tones are not properly extracted." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Try again with a different parameter for the maximum slope.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "xtract_tonal_params.maximum_slope = 5000.0\nxtract_tonal.process()\n\n# Recheck the plots\nprint(\"Plot of the spectrograms with the right tonal extraction parameters.\")\nplot_stft(stft_original, SPLmax=max_stft, title=\"Original Signal\")\n\n# Spectrogram of the modified signal\nstft_modified_signal.signal = xtract_tonal.get_output()[0]\nstft_modified_signal.process()\nplot_stft(stft_modified_signal, SPLmax=max_stft, title=\"Extracted Tones\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Transient extraction\r\n\r\nThe goal is to isolate the transients using the right settings. While\r\nthese settings are not as easy to handle, they are well explained in the\r\ntutorial videos installed with the Ansys Sound Analysis and\r\nSpecification (SAS) standalone application (with the user interface).\r\nYou can also find the [SAS - XTRACT\r\ntransient](https://learninghub.ansys.com/share/asset/view/108) videos on\r\nthe Ansys Learning Hub.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Create a set of transient parameters.\n# This example assumes that the best minimum and maximum thresholds are known.\n# You can use the SAS interface to help set up these thresholds interactively.\nxtract_transient_params = XtractTransientParameters(lower_threshold=51.5, upper_threshold=60.0)\n\n# Perform the transient extraction using the 'XtractTransient' class.\nxtract_transient = XtractTransient(\n input_signal=time_domain_signal, input_parameters=xtract_transient_params\n)\nxtract_transient.process()\ntransient_signal = xtract_transient.get_output()[0]\n\n# Plot the original signal and the transient signal in the same window\nplt.plot(time_vector, time_domain_signal.data, label=\"Original Signal\", linewidth=0.1)\nplt.plot(time_vector, transient_signal.data, label=\"Transient Signal\", linewidth=0.1)\nplt.grid(True)\nplt.xlabel(\"Time (s)\")\nplt.ylabel(\"Amplitude (Pa)\")\nplt.title(\"Original Signal and Transient signal\")\nleg = plt.legend()\nfor line in leg.get_lines():\n line.set_linewidth(0.5)\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Use a combination of extraction features and loop on several signals\r\n\r\nThe idea here is to loop over several signals and use the `Xtract` class\r\nto combine all previous classes.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "path_xtract_demo_signal_2 = download_xtract_demo_signal_2_wav()\n\npaths = [path_xtract_demo_signal_1, path_xtract_demo_signal_2]\n\n# Instantiate the 'Xtract' class with the parameters previously set\nxtract = Xtract(\n parameters_denoiser=xtract_denoiser_params,\n parameters_tonal=xtract_tonal_params,\n parameters_transient=xtract_transient_params,\n)\n\n# Loop over all signal paths contained in the 'paths' variable\nfor p in paths:\n # Name the signal using the file name\n signal_name = os.path.basename(p)\n\n # Load the signal\n wav_loader.path_to_wav = p\n wav_loader.process()\n time_domain_signal = wav_loader.get_output()[0]\n\n # Plot the time domain signal\n ylims = [-3.0, 3.0]\n plt.figure()\n plt.plot(time_vector, time_domain_signal.data, label=\"Original Signal\")\n plt.ylim(ylims)\n plt.ylabel(\"Amplitude (Pa)\")\n plt.xlabel(\"Time (s)\")\n plt.grid()\n plt.legend()\n plt.title(signal_name)\n plt.show()\n\n # Compute and plot the STFT\n stft_original.signal = time_domain_signal\n stft_original.process()\n plot_stft(stft_class=stft_original, SPLmax=max_stft, title=f\"STFT for signal {signal_name}\")\n\n # Use Xtract with the loaded signal\n xtract.input_signal = time_domain_signal\n xtract.process()\n\n # Collect outputs and plot everything in one window\n noise_signal, tonal_signal, transient_signal, remainder_signal = xtract.get_output()\n\n f, axs = plt.subplots(nrows=5)\n axs[0].plot(time_vector, time_domain_signal.data, label=\"Original Signal\", color=\"blue\")\n axs[1].plot(time_vector, noise_signal.data, label=\"Noise Signal\", color=\"red\")\n axs[2].plot(time_vector, tonal_signal.data, label=\"Tonal Signal\", color=\"green\")\n axs[2].set(ylabel=\"Amplitude (Pa)\") # Set ylabel for middle plot only\n axs[3].plot(time_vector, transient_signal.data, label=\"Transient Signal\", color=\"purple\")\n axs[4].plot(time_vector, remainder_signal.data, label=\"Remainder Signal\", color=\"black\")\n\n for ax in axs:\n ax.set_ylim(ylims)\n ax.grid()\n ax.legend()\n ax.set_aspect(\"auto\")\n\n plt.xlabel(\"Time (s)\")\n plt.legend()\n plt.suptitle(f\"Original and extracted signals for {signal_name}\")\n plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/version/0.1/_downloads/1c4428c77d0780247b51f77f0706272a/007_calculate_psychoacoustic_indicators.py b/version/0.1/_downloads/1c4428c77d0780247b51f77f0706272a/007_calculate_psychoacoustic_indicators.py new file mode 100644 index 000000000..3c23ce82d --- /dev/null +++ b/version/0.1/_downloads/1c4428c77d0780247b51f77f0706272a/007_calculate_psychoacoustic_indicators.py @@ -0,0 +1,221 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _calculate_psychoacoustic_indicators: + +Calculate psychoacoustic indicators +----------------------------------- + +This example shows how to calculate psychoacoustic indicators. +The following indicators are included: + +- Loudness of stationary sounds according to ISO 532-1. +- Loudness of time-varying sounds according to ISO 532-1. +- Sharpness according to Zwicker and Fastl, "Psychoacoustics: Facts and models", 1990. +- Roughness according to Daniel and Weber, "Psychoacoustical Roughness: Implementation of an + Optimized Model, 1997. +- Fluctuation strength according to Sontacchi, "Entwicklung eines Modulkonzeptes für die + psychoakustische Geräuschanalyse under MatLab Diplomarbeit", 1998. + +The example shows how to perform these operations: + +- Set up the analysis. +- Calculate indicators on loaded WAV files. +- Get calculation outputs. +- Plot some corresponding curves. + +""" + +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. + +# Load Ansys libraries. +import os + +import numpy as np + +from ansys.sound.core.examples_helpers import ( + download_accel_with_rpm_wav, + download_flute_2_wav, + download_flute_wav, +) +from ansys.sound.core.psychoacoustics import ( + FluctuationStrength, + LoudnessISO532_1_Stationary, + LoudnessISO532_1_TimeVarying, + Roughness, + Sharpness, +) +from ansys.sound.core.psychoacoustics.roughness import Roughness +from ansys.sound.core.psychoacoustics.sharpness import Sharpness +from ansys.sound.core.server_helpers import connect_to_or_start_server +from ansys.sound.core.signal_utilities import LoadWav + +# Connect to a remote server or start a local server. +server = connect_to_or_start_server() + + +# %% +# Calculate ISO 532-1 loudness for a stationary sound +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Load a signal from a WAV file using the ``LoadWav`` class. It is returned as a DPF +# field container. For more information, see `fields_container +# `_ +# in the DPF-Core API documentation. + +# Load example data from WAV file +path_flute_wav = download_flute_wav() +wav_loader = LoadWav(path_flute_wav) +wav_loader.process() +fc_signal = wav_loader.get_output() + +# %% +# Create a ``LoudnessISO532_1_Stationary`` object, set its signal, and compute the loudness. +loudness_stationary = LoudnessISO532_1_Stationary(signal=fc_signal) +loudness_stationary.process() + +# %% +# Get the value in sone or in phon. +loudness_sone = loudness_stationary.get_loudness_sone() +loudness_level_phon = loudness_stationary.get_loudness_level_phon() +file_name = os.path.basename(path_flute_wav) +print( + f"\nThe loudness of sound file {file_name} " + f"is{loudness_sone: .1f} sones " + f"or{loudness_level_phon: .1f} phons." +) + +# %% +# Plot the specific loudness. +loudness_stationary.plot() + +# %% +# Calculate ISO 532-1 loudness for several signals at once +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Load another WAV file and store it along with the first one. + +path_flute2_wav = download_flute_2_wav() +wav_loader = LoadWav(path_flute2_wav) +wav_loader.process() + +# Store the second signal as a second field in the DPF fields container. +fc_two_signals = fc_signal +fc_two_signals.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + +# %% +# Calculate the loudness for both signals at once. +loudness_stationary = LoudnessISO532_1_Stationary(signal=fc_two_signals) +loudness_stationary.process() + +# %% +# Get the values in sone or in phon. +loudness_sone2 = loudness_stationary.get_loudness_sone(1) +loudness_level_phon2 = loudness_stationary.get_loudness_level_phon(1) +file_name2 = os.path.basename(path_flute2_wav) +print( + f"In comparison, the loudness of sound file {file_name2} " + f"is{loudness_sone2: .1f} sones " + f"or{loudness_level_phon2: .1f} phons." +) + +# %% +# Plot specific loudness for both signals in a single figure. Note how the first sound has a +# higher specific loudness than the second. +loudness_stationary.plot() + +# %% +# Calculate ISO 532-1 loudness for a non-stationary sound +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Load a new signal (non-stationary) from a WAV file. +path_accel_wav = download_accel_with_rpm_wav() +wav_loader = LoadWav(path_accel_wav) +wav_loader.process() +f_signal = wav_loader.get_output()[0] # Field 0 only, because the RPM profile is useless here. + +# %% +# Create a ``LoudnessISO532_1_TimeVarying`` object, set its signal, and compute the loudness. +loudness_time_varying = LoudnessISO532_1_TimeVarying(signal=f_signal) +loudness_time_varying.process() + +# %% +# Get percentile loudness values. +N5 = loudness_time_varying.get_N5_sone() +N10 = loudness_time_varying.get_N10_sone() +L5 = loudness_time_varying.get_L5_phon() +L10 = loudness_time_varying.get_L10_phon() + +file_name3 = os.path.basename(path_accel_wav) +print( + f"\nThe sound file {file_name3} has the following percentile loudness values: \n" + f"- N5 = {np.round(N5, 1)} sones.\n" + f"- N10 = {np.round(N10, 1)} sones.\n" + f"- L5 = {np.round(L5, 1)} phons.\n" + f"- L10 = {np.round(L10, 1)} phons." +) + +# %% +# Plot loudness as a function of time. +loudness_time_varying.plot() + +# %% +# Calculate sharpness, roughness, and fluctuation strength +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Calculate sharpness, roughness, and fluctuation strength for the two sounds. + +# %% +# Calculate the sharpness. +sharpness = Sharpness(signal=fc_two_signals) +sharpness.process() +sharpness_values = (sharpness.get_sharpness(0), sharpness.get_sharpness(1)) + +# %% +# Calculate the roughness. +roughness = Roughness(signal=fc_two_signals) +roughness.process() +roughness_values = (roughness.get_roughness(0), roughness.get_roughness(1)) + +# %% +# Calculate the fluctuation strength. +fluctuation_strength = FluctuationStrength(signal=fc_two_signals) +fluctuation_strength.process() +fluctuation_strength_values = ( + fluctuation_strength.get_fluctuation_strength(0), + fluctuation_strength.get_fluctuation_strength(1), +) + +# %% +# Print the results. +print( + f"\nThe sharpness of sound file {file_name} " + f"is{sharpness_values[0]: .2f} acum, " + f"its roughness is{roughness_values[0]: .2f} asper, " + f"and its fluctuation strength is{fluctuation_strength_values[0]: .2f} vacil.\n" + f"For sound file {file_name2}, these indicators' values are, respectively, " + f"{sharpness_values[1]: .2f} acum, " + f"{roughness_values[1]: .2f} asper, " + f"and{fluctuation_strength_values[1]: .2f} vacil.\n" +) diff --git a/version/0.1/_downloads/1e223d03c73c8ed9149c8a39eae7c151/003_compute_stft.ipynb b/version/0.1/_downloads/1e223d03c73c8ed9149c8a39eae7c151/003_compute_stft.ipynb new file mode 100644 index 000000000..fc9519704 --- /dev/null +++ b/version/0.1/_downloads/1e223d03c73c8ed9149c8a39eae7c151/003_compute_stft.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Compute the STFT and ISTFT {#compute_stft_example}\r\n\r\nThis example shows how to compute the short-time Fourier transform\r\n(STFT) of a signal. It also shows how to compute the inverse short-time\r\nFourier transform (ISTFT) from a STFT matrix and get a signal.\r\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set up analysis\r\n\r\nSetting up the analysis consists of loading Ansys libraries, connecting\r\nto the DPF server, and retrieving the example files.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load Ansys libraries.\nfrom ansys.sound.core.examples_helpers import download_flute_wav\nfrom ansys.sound.core.server_helpers import connect_to_or_start_server\nfrom ansys.sound.core.signal_utilities import LoadWav\nfrom ansys.sound.core.spectrogram_processing import Istft, Stft\n\n# Connect to a remote server or start a local server\nmy_server = connect_to_or_start_server(use_license_context=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load a signal\r\n\r\nLoad a signal from a WAV file using the `LoadWav` class. It is returned\r\nas a DPF field container. For more information, see\r\n[fields_container](https://dpf.docs.pyansys.com/version/stable/api/ansys.dpf.core.operators.utility.fields_container.html)\r\nin the DPF-Core API documentation.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Return the input data of the example file\npath_flute_wav = download_flute_wav()\n\n# Load the WAV file\nwav_loader = LoadWav(path_flute_wav)\nwav_loader.process()\nfc_signal = wav_loader.get_output()\n\n# Plot the input signal\nwav_loader.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Compute and plot STFT\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Instantiate an instance of the ``Stft`` class using the previously loaded signal\n# as an input. Use an FFT size of 1024 points and then display the STFT colormap.\n\nstft = Stft(fc_signal, fft_size=1024)\n\n# Process the STFT\nstft.process()\n\n# Plot the output\nstft.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Modify the STFT parameters using the setters of the `Stft` class.\r\nDisplay the new STFT colormap.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "stft.fft_size = 4096\nstft.window_overlap = 0.95\nstft.window_type = \"BARTLETT\"\n\n# Reprocess the STFT with the new parameters\nstft.process()\n\n# Plot the modified output\nstft.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute and plot ISTFT \\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\\~\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Obtain a time-domain signal using the ``Istft`` class.\n# The input of the ``Istft`` class is the output STFT object previously computed.\n\nfc_stft = stft.get_output()\n\n# Instantiate the class\nistft = Istft(fc_stft)\n\n# Process the ISTFT\nistft.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the output, which is the original signal.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "istft.plot()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/version/0.1/_downloads/20149a7fd85f45918089684c122fe644/004_isolate_orders.py b/version/0.1/_downloads/20149a7fd85f45918089684c122fe644/004_isolate_orders.py new file mode 100644 index 000000000..5cac09724 --- /dev/null +++ b/version/0.1/_downloads/20149a7fd85f45918089684c122fe644/004_isolate_orders.py @@ -0,0 +1,273 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _isolate_orders_example: + +Isolate orders +-------------- + +Orders are harmonic and partial components in the sound related to the speed of a +rotating machine. This example shows how to isolate orders in a signal containing an RPM profile. +It also uses additional classes from PyAnsys Sound to compute spectrograms +and the loudness of the isolated signals. + +""" + +# Maximum frequency for STFT plots, change according to your need +MAX_FREQUENCY_PLOT_STFT = 2000.0 + +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. +# + +# Load Ansys libraries. +import os +import pathlib + +import matplotlib.pyplot as plt +import numpy as np + +from ansys.sound.core.examples_helpers import ( + download_accel_with_rpm_2_wav, + download_accel_with_rpm_3_wav, + download_accel_with_rpm_wav, +) +from ansys.sound.core.psychoacoustics import LoudnessISO532_1_Stationary +from ansys.sound.core.server_helpers import connect_to_or_start_server +from ansys.sound.core.signal_utilities import LoadWav, WriteWav +from ansys.sound.core.spectrogram_processing import IsolateOrders, Stft + +# Connect to a remote or start a local server +my_server = connect_to_or_start_server() + + +# %% +# Define a custom function for STFT plots +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Defining a custom function for STFT plots lets you have +# more control over what you are displaying. +# While you could use the ``Stft.plot()`` method, the custom function +# defined here restricts the frequency range of the plot. +def plot_stft(stft_class, vmax): + out = stft_class.get_output_as_nparray() + + # Extract first half of the STFT (second half is symmetrical) + half_nfft = int(out.shape[0] / 2) + 1 + magnitude = stft_class.get_stft_magnitude_as_nparray() + + # Voluntarily ignore a numpy warning + np.seterr(divide="ignore") + magnitude = 20 * np.log10(magnitude[0:half_nfft, :]) + np.seterr(divide="warn") + + # Obtain sampling frequency, time steps, and number of time samples + fs = 1.0 / ( + stft_class.signal.time_freq_support.time_frequencies.data[1] + - stft_class.signal.time_freq_support.time_frequencies.data[0] + ) + time_step = np.floor(stft_class.fft_size * (1.0 - stft_class.window_overlap) + 0.5) / fs + num_time_index = len(stft_class.get_output().get_available_ids_for_label("time")) + + # Define boundaries of the plot + extent = [0, time_step * num_time_index, 0.0, fs / 2.0] + + # Plot + plt.imshow( + magnitude, + origin="lower", + aspect="auto", + cmap="jet", + extent=extent, + vmin=vmax - 70.0, + vmax=vmax, + ) + plt.colorbar(label="Amplitude (dB SPL)") + plt.ylabel("Frequency (Hz)") + plt.xlabel("Time (s)") + plt.ylim( + [0.0, MAX_FREQUENCY_PLOT_STFT] + ) # Change the value of MAX_FREQUENCY_PLOT_STFT if needed + plt.title("STFT") + plt.show() + + +# %% +# Load a signal with an RPM profile +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Load a signal that has been generated with Ansys Sound Analysis +# and Specification (SAS) from a WAV file using the ``LoadWav`` class. +# This class contains two channels: +# +# - The actual signal (an acceleration recording) +# - The associated RPM profile + +# Return the input data of the example file +path_accel_wav = download_accel_with_rpm_wav() + +# Load the WAV file. +wav_loader = LoadWav(path_accel_wav) +wav_loader.process() +fc_signal = wav_loader.get_output() + +# Extract the audio signal and the RPM profile +wav_signal, rpm_signal = wav_loader.get_output_as_nparray() + +# Extract time support associated with the signal +time_support = fc_signal[0].time_freq_support.time_frequencies.data + +# Plot the signal and its associated RPM profile +fig, ax = plt.subplots(nrows=2, sharex=True) +ax[0].plot(time_support, wav_signal) +ax[0].set_title("Audio Signal") +ax[0].set_ylabel("Amplitude (Pa)") +ax[0].grid(True) +ax[1].plot(time_support, rpm_signal, color="red") +ax[1].set_title("RPM profile") +ax[1].set_ylabel("rpm") +ax[1].grid(True) +plt.xlabel("Time (s)") +plt.show() + +# %% +# Plot spectrogram of the original signal +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Plot the spectrogram of the original signal. + +stft = Stft(signal=fc_signal[0], window_overlap=0.9, fft_size=8192) +stft.process() +max_stft = 20 * np.log10(np.max(stft.get_stft_magnitude_as_nparray())) +plot_stft(stft, max_stft) + +# %% +# Isolate orders +# ~~~~~~~~~~~~~~~~ +# Isolate orders 2, 4, and 6 with the ``IsolateOrders`` class. + +field_wav, field_rpm = wav_loader.get_output() + +# Define parameters for order isolation +field_wav.unit = "Pa" +order_to_isolate = [2, 4, 6] # Orders indexes to isolate as a list +fft_size = 8192 # FFT Size (in samples) +window_type = "HANN" # Window type +window_overlap = 0.9 # Window overlap +width_selection = 3 # Width of the order selection in Hz + +# Instantiate the ``IsolateOrders`` class with the parameters +isolate_orders = IsolateOrders( + signal=field_wav, + rpm_profile=field_rpm, + orders=order_to_isolate, + fft_size=fft_size, + window_type=window_type, + window_overlap=window_overlap, + width_selection=width_selection, +) + +# Isolate orders +isolate_orders.process() + +# Plot the spectrogram of the isolated orders +stft.signal = isolate_orders.get_output() +stft.process() +plot_stft(stft, max_stft) + +# %% +# Isolate different orders +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# Change FFT size, order indexes, and window type. Then re-isolate the orders. + +# Change some parameters directly using the setters of the class +isolate_orders.orders = [2, 6] +isolate_orders.window_type = "BLACKMAN" + +# Reprocess (Must be called explicitly. Otherwise, the output won't be updated.) +isolate_orders.process() + + +# Plot the spectrogram of the isolated orders +stft.signal = isolate_orders.get_output() +stft.process() +plot_stft(stft, max_stft) +# %% +# Work with the isolated signal +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Plot the signal containing the isolated orders and compute its loudness. + +# Plot the signal directly using the method from the ``IsolateOrders`` class +isolate_orders.plot() + +# Use the ``Loudness`` class to compute the loudness of the isolate signal +input_loudness = isolate_orders.get_output() +input_loudness.unit = "Pa" +loudness = LoudnessISO532_1_Stationary(signal=input_loudness) +loudness.process() + +loudness_isolated_signal = loudness.get_loudness_level_phon() + +# Compute the loudness for the original signal +loudness.signal = field_wav +loudness.process() + +loudness_original_signal = loudness.get_loudness_level_phon() + +print(f"Loudness of the original signal is {loudness_original_signal: .1f} phons.") +print(f"Loudness of the isolated signal is {loudness_isolated_signal: .1f} phons.") + +# %% +# Isolate orders of several signals in a loop +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Loop over a list of given signals and write them as a WAV file. + +# Obtain parent folder of the 'accel_with_rpm.wav' file +parent_folder = pathlib.Path(path_accel_wav).parent.absolute() + +path_accel_wav_2 = download_accel_with_rpm_2_wav() +path_accel_wav_3 = download_accel_with_rpm_3_wav() +paths = (path_accel_wav, path_accel_wav_2, path_accel_wav_3) + +fft_sizes = [256, 2048, 4096] + +wav_writer = WriteWav() + +# Isolate orders for all the files containing RPM profiles in this folder +for file, fft_sz in zip(paths, fft_sizes): + # Loading the file + wav_loader.path_to_wav = file + wav_loader.process() + + # Set parameters for order isolation + isolate_orders.signal = wav_loader.get_output()[0] + isolate_orders.rpm_profile = wav_loader.get_output()[1] + isolate_orders.fft_size = fft_sz + isolate_orders.process() + + # Write as a WAV file on the disk + out_name = os.path.basename(file)[:-4] + "_isolated_fft_size_" + str(fft_sz) + ".wav" + path_to_write = parent_folder / out_name + wav_writer.path_to_write = str(path_to_write) + wav_writer.signal = isolate_orders.get_output() + wav_writer.process() diff --git a/version/0.1/_downloads/432877ab5914b4ced495ed3a071e2895/005_xtract_feature.py b/version/0.1/_downloads/432877ab5914b4ced495ed3a071e2895/005_xtract_feature.py new file mode 100644 index 000000000..79ff609c9 --- /dev/null +++ b/version/0.1/_downloads/432877ab5914b4ced495ed3a071e2895/005_xtract_feature.py @@ -0,0 +1,353 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _xtract_feature_example: + +Use the Xtract feature +---------------------- + +This example shows how to use the Xtract feature in PyAnsys Sound. +It demonstrates different capabilities of this feature, such as +noise extraction, tonal extraction, and transient extraction. + +""" + +# Maximum frequency for STFT plots, change according to your need +MAX_FREQUENCY_PLOT_STFT = 5000.0 + +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. + +# Load Ansys libraries. +import os + +import matplotlib.pyplot as plt +import numpy as np + +from ansys.sound.core.examples_helpers import ( + download_xtract_demo_signal_1_wav, + download_xtract_demo_signal_2_wav, +) +from ansys.sound.core.server_helpers import connect_to_or_start_server +from ansys.sound.core.signal_utilities import CropSignal, LoadWav +from ansys.sound.core.spectrogram_processing import Stft +from ansys.sound.core.xtract import ( + Xtract, + XtractDenoiser, + XtractDenoiserParameters, + XtractTonal, + XtractTonalParameters, + XtractTransient, + XtractTransientParameters, +) + +# Connect to a remote server or start a local server +my_server = connect_to_or_start_server(use_license_context=True) + + +# %% +# Define a custom function for STFT plots +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Define a custom function for STFT plots lets you have +# more control over what you are displaying. +# While you could use the ``Stft.plot()`` method, the custom function +# defined here restricts the frequency range of the plot. +def plot_stft(stft_class, SPLmax, title="STFT", maximum_frequency=MAX_FREQUENCY_PLOT_STFT): + """Plot a short-term Fourier transform (STFT) into a figure window. + + Parameters + ---------- + stft_class: Stft + Object containing the STFT. + SPLmax: float + Maximum value (here in dB SPL) for the colormap. + title: str + Title of the figure. + maximum_frequency: float + Maximum frequency in Hz to display. + """ + out = stft_class.get_output_as_nparray() + + # Extract first half of the STFT (second half is symmetrical) + half_nfft = int(out.shape[0] / 2) + 1 + magnitude = stft_class.get_stft_magnitude_as_nparray() + + # Voluntarily ignore a numpy warning + np.seterr(divide="ignore") + magnitude = 20 * np.log10(magnitude[0:half_nfft, :]) + np.seterr(divide="warn") + + # Obtain sampling frequency, time steps, and number of time samples + fs = 1.0 / ( + stft_class.signal.time_freq_support.time_frequencies.data[1] + - stft_class.signal.time_freq_support.time_frequencies.data[0] + ) + time_step = np.floor(stft_class.fft_size * (1.0 - stft_class.window_overlap) + 0.5) / fs + num_time_index = len(stft_class.get_output().get_available_ids_for_label("time")) + + # Define boundaries of the plot + extent = [0, time_step * num_time_index, 0.0, fs / 2.0] + + # Plot + plt.figure() + plt.imshow( + magnitude, + origin="lower", + aspect="auto", + cmap="jet", + extent=extent, + vmax=SPLmax, + vmin=(SPLmax - 70.0), + ) + plt.colorbar(label="Magnitude (dB SPL)") + plt.ylabel("Frequency (Hz)") + plt.xlabel("Time (s)") + plt.ylim([0.0, maximum_frequency]) # Change the value of MAX_FREQUENCY_PLOT_STFT if needed + plt.title(title) + plt.show() + + +# %% +# Load a demo signal for Xtract +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Load a demo signal from a WAV file using the ``LoadWav`` class. +# The WAV file contains harmonics and shocks. + +# Return the input data of the example file +path_xtract_demo_signal_1 = download_xtract_demo_signal_1_wav() + +# Load the WAV file +wav_loader = LoadWav(path_to_wav=path_xtract_demo_signal_1) +wav_loader.process() + +# Plot the signal in time domain +time_domain_signal = wav_loader.get_output()[0] +time_vector = time_domain_signal.time_freq_support.time_frequencies.data +plt.plot(time_vector, time_domain_signal.data) +plt.title("Xtract Demo Signal 1") +plt.grid(True) +plt.xlabel("Time (s)") +plt.ylabel("Amplitude (Pa)") +plt.show() + +# Compute the spectrogram of the signal and plot it +stft_original = Stft(signal=wav_loader.get_output()[0], fft_size=1024, window_overlap=0.9) +stft_original.process() +max_stft = 20 * np.log10(np.max(stft_original.get_stft_magnitude_as_nparray())) + +plot_stft(stft_original, SPLmax=max_stft, maximum_frequency=20000.0) + +# %% +# Use individual extraction features +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# The following topics show how to use different capabilities of Xtract independently. + +# %% +# Noise extraction +# ^^^^^^^^^^^^^^^^ +# The goal is to isolate a fan noise deprived of any tonal content in the demo signal. + +# Create a noise pattern using the first two seconds of the signal. +# First crop the first two seconds of the signal. +signal_cropper = CropSignal(signal=time_domain_signal, start_time=0.0, end_time=2.0) +signal_cropper.process() +cropped_signal = signal_cropper.get_output() + +# Then use the 'XtractDenoiserParameters' class to create the noise pattern. +xtract_denoiser_params = XtractDenoiserParameters() +xtract_denoiser_params.noise_psd = xtract_denoiser_params.create_noise_psd_from_noise_samples( + signal=cropped_signal, sampling_frequency=44100.0, window_length=100 +) + +# Denoise the signal using the 'XtractDenoiser' class. +xtract_denoiser = XtractDenoiser( + input_signal=time_domain_signal, input_parameters=xtract_denoiser_params +) +xtract_denoiser.process() + +noise_signal = xtract_denoiser.get_output()[1] + +# Plot the original signal and the noise signal in the same window +plt.plot(time_vector, time_domain_signal.data, label="Original Signal") +plt.plot(time_vector, noise_signal.data, label="Noise Signal") +plt.grid(True) +plt.xlabel("Time (s)") +plt.ylabel("Amplitude (Pa)") +plt.title("Original Signal and Noise Signal") +plt.legend() +plt.show() + +# %% +# Tone extraction +# ^^^^^^^^^^^^^^^ +# The goal is to isolate the tones using the right settings. + +# Try a first attempt of tone extraction with a +# First set of parameters using the 'XtractTonalParameters' class. +xtract_tonal_params = XtractTonalParameters() +xtract_tonal_params.regularity = 1.0 +xtract_tonal_params.maximum_slope = 1000.0 +xtract_tonal_params.minimum_duration = 0.22 +xtract_tonal_params.intertonal_gap = 10.0 +xtract_tonal_params.local_emergence = 2.0 +xtract_tonal_params.fft_size = 2048 + +## Now perform the tonal extraction using the 'XtractTonal' class +xtract_tonal = XtractTonal(input_signal=time_domain_signal, input_parameters=xtract_tonal_params) +xtract_tonal.process() + +# %% +# Plot the spectrogram to assess the quality of the output. +stft_modified_signal = Stft(signal=xtract_tonal.get_output()[0], fft_size=1024, window_overlap=0.9) +stft_modified_signal.process() + +print("Plot of the spectrograms with tonal extraction parameters that do not work.") + +## Spectrogram of the original signal +plot_stft(stft_original, SPLmax=max_stft, title="Original Signal") + +## Spectrogram of the modified signal +plot_stft(stft_modified_signal, SPLmax=max_stft, title="Extracted Tones") +# You can see from the obtained plot that the tones are not properly extracted. + +# %% +# Try again with a different parameter for the maximum slope. +xtract_tonal_params.maximum_slope = 5000.0 +xtract_tonal.process() + +# Recheck the plots +print("Plot of the spectrograms with the right tonal extraction parameters.") +plot_stft(stft_original, SPLmax=max_stft, title="Original Signal") + +# Spectrogram of the modified signal +stft_modified_signal.signal = xtract_tonal.get_output()[0] +stft_modified_signal.process() +plot_stft(stft_modified_signal, SPLmax=max_stft, title="Extracted Tones") + +# %% +# Transient extraction +# ^^^^^^^^^^^^^^^^^^^^ +# The goal is to isolate the transients using the right settings. +# While these settings are not as easy to handle, they are well explained in the tutorial videos +# installed with the Ansys Sound Analysis and Specification (SAS) standalone +# application (with the user interface). You can also find the +# `SAS - XTRACT transient `_ +# videos on the Ansys Learning Hub. + +# Create a set of transient parameters. +# This example assumes that the best minimum and maximum thresholds are known. +# You can use the SAS interface to help set up these thresholds interactively. +xtract_transient_params = XtractTransientParameters(lower_threshold=51.5, upper_threshold=60.0) + +# Perform the transient extraction using the 'XtractTransient' class. +xtract_transient = XtractTransient( + input_signal=time_domain_signal, input_parameters=xtract_transient_params +) +xtract_transient.process() +transient_signal = xtract_transient.get_output()[0] + +# Plot the original signal and the transient signal in the same window +plt.plot(time_vector, time_domain_signal.data, label="Original Signal", linewidth=0.1) +plt.plot(time_vector, transient_signal.data, label="Transient Signal", linewidth=0.1) +plt.grid(True) +plt.xlabel("Time (s)") +plt.ylabel("Amplitude (Pa)") +plt.title("Original Signal and Transient signal") +leg = plt.legend() +for line in leg.get_lines(): + line.set_linewidth(0.5) +plt.show() + +# %% +# Use a combination of extraction features and loop on several signals +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# The idea here is to loop over several signals and use the ``Xtract`` class to combine +# all previous classes. + + +path_xtract_demo_signal_2 = download_xtract_demo_signal_2_wav() + +paths = [path_xtract_demo_signal_1, path_xtract_demo_signal_2] + +# Instantiate the 'Xtract' class with the parameters previously set +xtract = Xtract( + parameters_denoiser=xtract_denoiser_params, + parameters_tonal=xtract_tonal_params, + parameters_transient=xtract_transient_params, +) + +# Loop over all signal paths contained in the 'paths' variable +for p in paths: + # Name the signal using the file name + signal_name = os.path.basename(p) + + # Load the signal + wav_loader.path_to_wav = p + wav_loader.process() + time_domain_signal = wav_loader.get_output()[0] + + # Plot the time domain signal + ylims = [-3.0, 3.0] + plt.figure() + plt.plot(time_vector, time_domain_signal.data, label="Original Signal") + plt.ylim(ylims) + plt.ylabel("Amplitude (Pa)") + plt.xlabel("Time (s)") + plt.grid() + plt.legend() + plt.title(signal_name) + plt.show() + + # Compute and plot the STFT + stft_original.signal = time_domain_signal + stft_original.process() + plot_stft(stft_class=stft_original, SPLmax=max_stft, title=f"STFT for signal {signal_name}") + + # Use Xtract with the loaded signal + xtract.input_signal = time_domain_signal + xtract.process() + + # Collect outputs and plot everything in one window + noise_signal, tonal_signal, transient_signal, remainder_signal = xtract.get_output() + + f, axs = plt.subplots(nrows=5) + axs[0].plot(time_vector, time_domain_signal.data, label="Original Signal", color="blue") + axs[1].plot(time_vector, noise_signal.data, label="Noise Signal", color="red") + axs[2].plot(time_vector, tonal_signal.data, label="Tonal Signal", color="green") + axs[2].set(ylabel="Amplitude (Pa)") # Set ylabel for middle plot only + axs[3].plot(time_vector, transient_signal.data, label="Transient Signal", color="purple") + axs[4].plot(time_vector, remainder_signal.data, label="Remainder Signal", color="black") + + for ax in axs: + ax.set_ylim(ylims) + ax.grid() + ax.legend() + ax.set_aspect("auto") + + plt.xlabel("Time (s)") + plt.legend() + plt.suptitle(f"Original and extracted signals for {signal_name}") + plt.show() diff --git a/version/0.1/_downloads/5d2d999ed464bb41dc9b7f6c419e1c47/002_load_resample_amplify_write_wav_files.py b/version/0.1/_downloads/5d2d999ed464bb41dc9b7f6c419e1c47/002_load_resample_amplify_write_wav_files.py new file mode 100644 index 000000000..48197424a --- /dev/null +++ b/version/0.1/_downloads/5d2d999ed464bb41dc9b7f6c419e1c47/002_load_resample_amplify_write_wav_files.py @@ -0,0 +1,127 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _load_resample_amplify_write_wav_files_example: + +Load a signal from a WAV file +----------------------------- + +This example shows how to load a signal from a WAV file, modify the signal's +sampling frequency, and amplify it. It also shows how to access the corresponding +data, display it using Matplotlib, and then write the modified signal to the disk as +a new WAV file. + +""" +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. + +# Load Ansys and other libraries. +import matplotlib.pyplot as plt + +from ansys.sound.core.examples_helpers import download_flute_wav +from ansys.sound.core.server_helpers import connect_to_or_start_server +from ansys.sound.core.signal_utilities import ApplyGain, LoadWav, Resample, WriteWav + +# Connect to a remote server or start a local server +server = connect_to_or_start_server() + +# %% +# Load a signal +# ~~~~~~~~~~~~~ +# Load a signal from a WAV file using the ``LoadWav`` class. It is returned as a DPF +# fields container. For more information, see `fields_container +# `_ +# in the DPF-Core API documentation. + +# Return the input data of the example file +path_flute_wav = download_flute_wav() + +# Load the WAV file. +wav_loader = LoadWav(path_flute_wav) +wav_loader.process() +fc_signal_original = wav_loader.get_output() + +t1 = fc_signal_original[0].time_freq_support.time_frequencies.data +sf1 = 1.0 / (t1[1] - t1[0]) +print(f"The sampling frequency of the original signal is {int(sf1)} Hz.") + +# %% +# Resample the signal +# ~~~~~~~~~~~~~~~~~~~ +# Change the sampling frequency of the loaded signal. +resampler = Resample(fc_signal_original, new_sampling_frequency=20000.0) +resampler.process() +fc_signal_resampled = resampler.get_output() + +t2 = fc_signal_resampled[0].time_freq_support.time_frequencies.data +sf2 = 1.0 / (t2[1] - t2[0]) +print(f"The new sampling frequency of the signal is {int(sf2)} Hz.") + +# %% +# Apply a gain to the signal +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Amplify the resampled signal by 10 decibels. +gain = 10.0 +gain_applier = ApplyGain(fc_signal_resampled, gain=gain, gain_in_db=True) +gain_applier.process() +fc_signal_modified = gain_applier.get_output() + +# %% +# Plot signals +# ~~~~~~~~~~~~ +# Plot both the original signal and modified signal. + +# Get the signals as nparray +data_original = wav_loader.get_output_as_nparray() +data_modified = gain_applier.get_output_as_nparray() + +# Prepare the figure +fig, axs = plt.subplots(2) +fig.suptitle("Signals") + +axs[0].plot(t1, data_original, color="g", label=f"original signal, sf={int(sf1)} Hz") +axs[0].set_ylabel("Pa") +axs[0].legend(loc="upper right") +axs[0].set_ylim([-3, 3]) + +axs[1].plot( + t2, data_modified, color="r", label=f"modified signal, sf={int(sf2)} Hz, gain={gain} dBSPL" +) +axs[1].set_xlabel("Time(s)") +axs[1].set_ylabel("Amplitude(Pa)") +axs[1].legend(loc="upper right") +axs[1].set_ylim([-3, 3]) + +# Display the figure +plt.show() + +# %% +# Write the signal as a WAV file +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Write the modified signal to the disk as a WAV file. +output_path = path_flute_wav[:-4] + "_modified.wav" # "[-4]" is to remove the ".wav" extension +wav_writer = WriteWav(path_to_write=output_path, signal=fc_signal_modified, bit_depth="int16") +wav_writer.process() diff --git a/version/0.1/_downloads/70ed478534f57625dc1b75c9c910028f/002_load_resample_amplify_write_wav_files.ipynb b/version/0.1/_downloads/70ed478534f57625dc1b75c9c910028f/002_load_resample_amplify_write_wav_files.ipynb new file mode 100644 index 000000000..49f28fe98 --- /dev/null +++ b/version/0.1/_downloads/70ed478534f57625dc1b75c9c910028f/002_load_resample_amplify_write_wav_files.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load a signal from a WAV file {#load_resample_amplify_write_wav_files_example}\r\n\r\nThis example shows how to load a signal from a WAV file, modify the\r\nsignal\\'s sampling frequency, and amplify it. It also shows how to\r\naccess the corresponding data, display it using Matplotlib, and then\r\nwrite the modified signal to the disk as a new WAV file.\r\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set up analysis\r\n\r\nSetting up the analysis consists of loading Ansys libraries, connecting\r\nto the DPF server, and retrieving the example files.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load Ansys and other libraries.\nimport matplotlib.pyplot as plt\n\nfrom ansys.sound.core.examples_helpers import download_flute_wav\nfrom ansys.sound.core.server_helpers import connect_to_or_start_server\nfrom ansys.sound.core.signal_utilities import ApplyGain, LoadWav, Resample, WriteWav\n\n# Connect to a remote server or start a local server\nserver = connect_to_or_start_server()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load a signal\r\n\r\nLoad a signal from a WAV file using the `LoadWav` class. It is returned\r\nas a DPF fields container. For more information, see\r\n[fields_container](https://dpf.docs.pyansys.com/version/stable/api/ansys.dpf.core.operators.utility.fields_container.html)\r\nin the DPF-Core API documentation.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Return the input data of the example file\npath_flute_wav = download_flute_wav()\n\n# Load the WAV file.\nwav_loader = LoadWav(path_flute_wav)\nwav_loader.process()\nfc_signal_original = wav_loader.get_output()\n\nt1 = fc_signal_original[0].time_freq_support.time_frequencies.data\nsf1 = 1.0 / (t1[1] - t1[0])\nprint(f\"The sampling frequency of the original signal is {int(sf1)} Hz.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Resample the signal\r\n\r\nChange the sampling frequency of the loaded signal.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "resampler = Resample(fc_signal_original, new_sampling_frequency=20000.0)\nresampler.process()\nfc_signal_resampled = resampler.get_output()\n\nt2 = fc_signal_resampled[0].time_freq_support.time_frequencies.data\nsf2 = 1.0 / (t2[1] - t2[0])\nprint(f\"The new sampling frequency of the signal is {int(sf2)} Hz.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Apply a gain to the signal\r\n\r\nAmplify the resampled signal by 10 decibels.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "gain = 10.0\ngain_applier = ApplyGain(fc_signal_resampled, gain=gain, gain_in_db=True)\ngain_applier.process()\nfc_signal_modified = gain_applier.get_output()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot signals\r\n\r\nPlot both the original signal and modified signal.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Get the signals as nparray\ndata_original = wav_loader.get_output_as_nparray()\ndata_modified = gain_applier.get_output_as_nparray()\n\n# Prepare the figure\nfig, axs = plt.subplots(2)\nfig.suptitle(\"Signals\")\n\naxs[0].plot(t1, data_original, color=\"g\", label=f\"original signal, sf={int(sf1)} Hz\")\naxs[0].set_ylabel(\"Pa\")\naxs[0].legend(loc=\"upper right\")\naxs[0].set_ylim([-3, 3])\n\naxs[1].plot(\n t2, data_modified, color=\"r\", label=f\"modified signal, sf={int(sf2)} Hz, gain={gain} dBSPL\"\n)\naxs[1].set_xlabel(\"Time(s)\")\naxs[1].set_ylabel(\"Amplitude(Pa)\")\naxs[1].legend(loc=\"upper right\")\naxs[1].set_ylim([-3, 3])\n\n# Display the figure\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Write the signal as a WAV file\r\n\r\nWrite the modified signal to the disk as a WAV file.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "output_path = path_flute_wav[:-4] + \"_modified.wav\" # \"[-4]\" is to remove the \".wav\" extension\nwav_writer = WriteWav(path_to_write=output_path, signal=fc_signal_modified, bit_depth=\"int16\")\nwav_writer.process()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/version/0.1/_downloads/857fe7308cd433b8e4ae8cdb3e3ee922/006_calculate_PR_and_TNR.ipynb b/version/0.1/_downloads/857fe7308cd433b8e4ae8cdb3e3ee922/006_calculate_PR_and_TNR.ipynb new file mode 100644 index 000000000..12a023947 --- /dev/null +++ b/version/0.1/_downloads/857fe7308cd433b8e4ae8cdb3e3ee922/006_calculate_PR_and_TNR.ipynb @@ -0,0 +1,302 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate TNR and PR {#calculate_PR_and_TNR}\r\n\r\nThis example shows how to calculate tone-to-noise ratio (TNR) and\r\nprominence ratio (PR) following the ECMA 418-1 and ISO 7779 standards.\r\nIt also extracts the desired TNR and PR information.\r\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set up analysis\r\n\r\nSetting up the analysis consists of loading Ansys libraries, connecting\r\nto the DPF server, and retrieving the example files.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load Ansys libraries.\nfrom ansys.dpf.core import TimeFreqSupport, fields_factory, locations\nimport numpy as np\n\nfrom ansys.sound.core.examples_helpers import download_flute_psd, download_flute_wav\nfrom ansys.sound.core.psychoacoustics import ProminenceRatio, ToneToNoiseRatio\nfrom ansys.sound.core.server_helpers import connect_to_or_start_server\nfrom ansys.sound.core.signal_utilities import LoadWav\nfrom ansys.sound.core.spectral_processing import PowerSpectralDensity\n\n# Connect to a remote server or start a local server.\nserver = connect_to_or_start_server()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate TNR from a PSD\r\n\r\nLoad a power spectral density (PSD) stored as a text file and then use\r\nit to create a field that serves as an input for the TNR calculation.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load the PSD contained in an ASCII file. This file has two columns: 'Frequency (Hz)'\n# and 'PSD amplitude (dB SPL/Hz)'. The data is located in\n# \"C:\\Users\\username\\AppData\\Local\\Ansys\\ansys_sound_core\\examples\\\".\npath_flute_psd = download_flute_psd()\nfid = open(path_flute_psd)\nfid.readline() # Skip the first line (header)\nall_lines = fid.readlines()\nfid.close()\n\n# Create the array of PSD amplitude values.\npsd_dBSPL_per_Hz = []\nfrequencies_original = []\nfor line in all_lines:\n splitted_line = line.split()\n psd_dBSPL_per_Hz.append(float(splitted_line[1]))\n frequencies_original.append(float(splitted_line[0]))\n\n# Convert amplitudes in dBSPL/Hz into power in Pa^2/Hz.\npsd_dBSPL_per_Hz = np.array(psd_dBSPL_per_Hz)\npsd_Pa2_per_Hz = np.power(10, psd_dBSPL_per_Hz / 10) * 4e-10\n\n# The TNR/PR operators require the frequency array to be regularly spaced.\n# Thus, the original frequencies are interpolated to regularly spaced points.\nfrequencies_interp = np.linspace(0, 22050, len(frequencies_original))\npsd_Pa2_per_Hz_interp = np.interp(frequencies_interp, frequencies_original, psd_Pa2_per_Hz)\n\n# Create the input PSD field for computation of TNR and PR.\nf_psd = fields_factory.create_scalar_field(num_entities=1, location=locations.time_freq)\nf_psd.append(psd_Pa2_per_Hz_interp, 1)\n\n# Create and include a field containing the array of frequencies.\nsupport = TimeFreqSupport()\nf_frequencies = fields_factory.create_scalar_field(num_entities=1, location=locations.time_freq)\nf_frequencies.append(frequencies_interp, 1)\nsupport.time_frequencies = f_frequencies\nf_psd.time_freq_support = support" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a `ToneToNoiseRatio` object, set the created PSD field as input,\r\nand compute the TNR.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "tone_to_noise_ratio = ToneToNoiseRatio(psd=f_psd)\ntone_to_noise_ratio.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print results.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "number_tones = tone_to_noise_ratio.get_nb_tones()\nTNR = tone_to_noise_ratio.get_max_TNR_value()\nTNR_frequencies = tone_to_noise_ratio.get_peaks_frequencies()\nTNR_values = tone_to_noise_ratio.get_TNR_values()\nTNR_levels = tone_to_noise_ratio.get_peaks_levels()\n\nprint(\n f\"\\n\"\n f\"Number of tones found: {number_tones}\\n\"\n f\"Maximum TNR value: {np.round(TNR, 1)} dB\\n\"\n f\"All detected peaks' frequencies (Hz): \"\n f\"{np.round(TNR_frequencies)}\\n\"\n f\"All peaks' TNR values (dB): {np.round(TNR_values, 1)}\\n\"\n f\"All peaks' absolute levels (dB SPL): {np.round(TNR_levels, 1)}\\n\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the TNR over frequency.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "tone_to_noise_ratio.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Recalculate the TNR for specific frequencies.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "frequencies_i = [261, 525, 786, 1836]\ntone_to_noise_ratio = ToneToNoiseRatio(psd=f_psd, frequency_list=frequencies_i)\ntone_to_noise_ratio.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print information for a specific detected peak.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "tone_to_noise_ratio_525 = tone_to_noise_ratio.get_single_tone_info(tone_index=1)\nTNR_frequency = tone_to_noise_ratio_525[0]\nTNR_width = tone_to_noise_ratio_525[4] - tone_to_noise_ratio_525[3]\nTNR = tone_to_noise_ratio_525[1]\nprint(\n f\"\\n\"\n f\"TNR info for peak at ~525 Hz: \\n\"\n f\"Exact tone frequency: {round(TNR_frequency, 2)} Hz\\n\"\n f\"Tone width: {round(TNR_width, 2)} Hz\\n\"\n f\"TNR value: {round(TNR, 2)} dB\\n\\n\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate PR from a PSD\r\n\r\nUse the PowerSpectralDensity class to calculate a PSD, and compute\r\nProminence Ratio (PR).\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load example data from WAV file.\npath_flute_wav = download_flute_wav()\nwav_loader = LoadWav(path_flute_wav)\nwav_loader.process()\nflute_signal = wav_loader.get_output()[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a PowerSpectralDensity object, set its input signal and\r\nparameters, and compute the PSD.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "psd_object = PowerSpectralDensity(\n flute_signal, fft_size=8192, window_type=\"HANN\", window_length=8192, overlap=0.8\n)\npsd_object.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Get the computed PSD as a Field.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "f_psd = psd_object.get_output()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a ProminenceRatio object, set the computed PSD as input, and\r\ncompute the PR.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "prominence_ratio = ProminenceRatio(psd=f_psd)\nprominence_ratio.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print the results.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "number_tones = prominence_ratio.get_nb_tones()\nPR = prominence_ratio.get_max_PR_value()\nPR_frequencies = prominence_ratio.get_peaks_frequencies()\nPR_values = prominence_ratio.get_PR_values()\nPR_levels = prominence_ratio.get_peaks_levels()\nprint(\n f\"\\n\"\n f\"Number of tones found: {number_tones}\\n\"\n f\"Maximum PR value: {np.round(PR, 1)} dB\\n\"\n f\"All detected peaks' frequencies (Hz): {np.round(PR_frequencies)}\\n\"\n f\"All peaks' PR values (dB): {np.round(PR_values, 1)}\\n\"\n f\"All peaks' absolute levels (dB SPL): {np.round(PR_levels, 1)}\\n\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the PR as a function of frequency.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "prominence_ratio.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Recalculate the PR for specific frequencies.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "frequencies_i = [261, 525, 786, 1836]\nprominence_ratio = ProminenceRatio(psd=f_psd, frequency_list=frequencies_i)\nprominence_ratio.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print information for a specific detected peak.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "prominence_ratio_786 = prominence_ratio.get_single_tone_info(tone_index=2)\nPR_frequency = prominence_ratio_786[0]\nPR_width = prominence_ratio_786[4] - prominence_ratio_786[3]\nPR = prominence_ratio_786[1]\nprint(\n f\"\\n\"\n f\"PR info for peak at ~786 Hz: \\n\"\n f\"Exact tone frequency: {round(PR_frequency, 2)} Hz\\n\"\n f\"Tone width: {round(PR_width, 2)} Hz\\n\"\n f\"PR value: {round(PR, 2)} dB\\n\"\n)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/version/0.1/_downloads/94c8c704c0aba658deec404f8c60d60a/007_calculate_psychoacoustic_indicators.ipynb b/version/0.1/_downloads/94c8c704c0aba658deec404f8c60d60a/007_calculate_psychoacoustic_indicators.ipynb new file mode 100644 index 000000000..4b8eb5bac --- /dev/null +++ b/version/0.1/_downloads/94c8c704c0aba658deec404f8c60d60a/007_calculate_psychoacoustic_indicators.ipynb @@ -0,0 +1,345 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate psychoacoustic indicators {#calculate_psychoacoustic_indicators}\r\n\r\nThis example shows how to calculate psychoacoustic indicators. The\r\nfollowing indicators are included:\r\n\r\n- Loudness of stationary sounds according to ISO 532-1.\r\n- Loudness of time-varying sounds according to ISO 532-1.\r\n- Sharpness according to Zwicker and Fastl, \\\"Psychoacoustics: Facts\r\n and models\\\", 1990.\r\n- Roughness according to Daniel and Weber, \\\"Psychoacoustical\r\n Roughness: Implementation of an Optimized Model, 1997.\r\n- Fluctuation strength according to Sontacchi, \\\"Entwicklung eines\r\n Modulkonzeptes f\u00fcr die psychoakustische Ger\u00e4uschanalyse under MatLab\r\n Diplomarbeit\\\", 1998.\r\n\r\nThe example shows how to perform these operations:\r\n\r\n- Set up the analysis.\r\n- Calculate indicators on loaded WAV files.\r\n- Get calculation outputs.\r\n- Plot some corresponding curves.\r\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set up analysis\r\n\r\nSetting up the analysis consists of loading Ansys libraries, connecting\r\nto the DPF server, and retrieving the example files.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load Ansys libraries.\nimport os\n\nimport numpy as np\n\nfrom ansys.sound.core.examples_helpers import (\n download_accel_with_rpm_wav,\n download_flute_2_wav,\n download_flute_wav,\n)\nfrom ansys.sound.core.psychoacoustics import (\n FluctuationStrength,\n LoudnessISO532_1_Stationary,\n LoudnessISO532_1_TimeVarying,\n Roughness,\n Sharpness,\n)\nfrom ansys.sound.core.psychoacoustics.roughness import Roughness\nfrom ansys.sound.core.psychoacoustics.sharpness import Sharpness\nfrom ansys.sound.core.server_helpers import connect_to_or_start_server\nfrom ansys.sound.core.signal_utilities import LoadWav\n\n# Connect to a remote server or start a local server.\nserver = connect_to_or_start_server()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate ISO 532-1 loudness for a stationary sound\r\n\r\nLoad a signal from a WAV file using the `LoadWav` class. It is returned\r\nas a DPF field container. For more information, see\r\n[fields_container](https://dpf.docs.pyansys.com/version/stable/api/ansys.dpf.core.operators.utility.fields_container.html)\r\nin the DPF-Core API documentation.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load example data from WAV file\npath_flute_wav = download_flute_wav()\nwav_loader = LoadWav(path_flute_wav)\nwav_loader.process()\nfc_signal = wav_loader.get_output()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a `LoudnessISO532_1_Stationary` object, set its signal, and\r\ncompute the loudness.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_stationary = LoudnessISO532_1_Stationary(signal=fc_signal)\nloudness_stationary.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Get the value in sone or in phon.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_sone = loudness_stationary.get_loudness_sone()\nloudness_level_phon = loudness_stationary.get_loudness_level_phon()\nfile_name = os.path.basename(path_flute_wav)\nprint(\n f\"\\nThe loudness of sound file {file_name} \"\n f\"is{loudness_sone: .1f} sones \"\n f\"or{loudness_level_phon: .1f} phons.\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the specific loudness.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_stationary.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate ISO 532-1 loudness for several signals at once\r\n\r\nLoad another WAV file and store it along with the first one.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "path_flute2_wav = download_flute_2_wav()\nwav_loader = LoadWav(path_flute2_wav)\nwav_loader.process()\n\n# Store the second signal as a second field in the DPF fields container.\nfc_two_signals = fc_signal\nfc_two_signals.add_field({\"channel_number\": 1}, wav_loader.get_output()[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calculate the loudness for both signals at once.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_stationary = LoudnessISO532_1_Stationary(signal=fc_two_signals)\nloudness_stationary.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Get the values in sone or in phon.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_sone2 = loudness_stationary.get_loudness_sone(1)\nloudness_level_phon2 = loudness_stationary.get_loudness_level_phon(1)\nfile_name2 = os.path.basename(path_flute2_wav)\nprint(\n f\"In comparison, the loudness of sound file {file_name2} \"\n f\"is{loudness_sone2: .1f} sones \"\n f\"or{loudness_level_phon2: .1f} phons.\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot specific loudness for both signals in a single figure. Note how the\r\nfirst sound has a higher specific loudness than the second.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_stationary.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate ISO 532-1 loudness for a non-stationary sound\r\n\r\nLoad a new signal (non-stationary) from a WAV file.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "path_accel_wav = download_accel_with_rpm_wav()\nwav_loader = LoadWav(path_accel_wav)\nwav_loader.process()\nf_signal = wav_loader.get_output()[0] # Field 0 only, because the RPM profile is useless here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a `LoudnessISO532_1_TimeVarying` object, set its signal, and\r\ncompute the loudness.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_time_varying = LoudnessISO532_1_TimeVarying(signal=f_signal)\nloudness_time_varying.process()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Get percentile loudness values.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "N5 = loudness_time_varying.get_N5_sone()\nN10 = loudness_time_varying.get_N10_sone()\nL5 = loudness_time_varying.get_L5_phon()\nL10 = loudness_time_varying.get_L10_phon()\n\nfile_name3 = os.path.basename(path_accel_wav)\nprint(\n f\"\\nThe sound file {file_name3} has the following percentile loudness values: \\n\"\n f\"- N5 = {np.round(N5, 1)} sones.\\n\"\n f\"- N10 = {np.round(N10, 1)} sones.\\n\"\n f\"- L5 = {np.round(L5, 1)} phons.\\n\"\n f\"- L10 = {np.round(L10, 1)} phons.\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot loudness as a function of time.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "loudness_time_varying.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calculate sharpness, roughness, and fluctuation strength\r\n\r\nCalculate sharpness, roughness, and fluctuation strength for the two\r\nsounds.\r\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calculate the sharpness.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sharpness = Sharpness(signal=fc_two_signals)\nsharpness.process()\nsharpness_values = (sharpness.get_sharpness(0), sharpness.get_sharpness(1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calculate the roughness.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "roughness = Roughness(signal=fc_two_signals)\nroughness.process()\nroughness_values = (roughness.get_roughness(0), roughness.get_roughness(1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calculate the fluctuation strength.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fluctuation_strength = FluctuationStrength(signal=fc_two_signals)\nfluctuation_strength.process()\nfluctuation_strength_values = (\n fluctuation_strength.get_fluctuation_strength(0),\n fluctuation_strength.get_fluctuation_strength(1),\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print the results.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\n f\"\\nThe sharpness of sound file {file_name} \"\n f\"is{sharpness_values[0]: .2f} acum, \"\n f\"its roughness is{roughness_values[0]: .2f} asper, \"\n f\"and its fluctuation strength is{fluctuation_strength_values[0]: .2f} vacil.\\n\"\n f\"For sound file {file_name2}, these indicators' values are, respectively, \"\n f\"{sharpness_values[1]: .2f} acum, \"\n f\"{roughness_values[1]: .2f} asper, \"\n f\"and{fluctuation_strength_values[1]: .2f} vacil.\\n\"\n)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/version/0.1/_downloads/a402b2aaea1c3b46efe2fb35a3d34273/006_calculate_PR_and_TNR.py b/version/0.1/_downloads/a402b2aaea1c3b46efe2fb35a3d34273/006_calculate_PR_and_TNR.py new file mode 100644 index 000000000..911ecdd85 --- /dev/null +++ b/version/0.1/_downloads/a402b2aaea1c3b46efe2fb35a3d34273/006_calculate_PR_and_TNR.py @@ -0,0 +1,208 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _calculate_PR_and_TNR: + +Calculate TNR and PR +-------------------- + +This example shows how to calculate tone-to-noise ratio (TNR) and prominence +ratio (PR) following the ECMA 418-1 and ISO 7779 standards. It also extracts +the desired TNR and PR information. +""" + +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. + +# Load Ansys libraries. +from ansys.dpf.core import TimeFreqSupport, fields_factory, locations +import numpy as np + +from ansys.sound.core.examples_helpers import download_flute_psd, download_flute_wav +from ansys.sound.core.psychoacoustics import ProminenceRatio, ToneToNoiseRatio +from ansys.sound.core.server_helpers import connect_to_or_start_server +from ansys.sound.core.signal_utilities import LoadWav +from ansys.sound.core.spectral_processing import PowerSpectralDensity + +# Connect to a remote server or start a local server. +server = connect_to_or_start_server() + +# %% +# Calculate TNR from a PSD +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# Load a power spectral density (PSD) stored as a text file and then use it to create +# a field that serves as an input for the TNR calculation. + +# Load the PSD contained in an ASCII file. This file has two columns: 'Frequency (Hz)' +# and 'PSD amplitude (dB SPL/Hz)'. The data is located in +# "C:\Users\username\AppData\Local\Ansys\ansys_sound_core\examples\". +path_flute_psd = download_flute_psd() +fid = open(path_flute_psd) +fid.readline() # Skip the first line (header) +all_lines = fid.readlines() +fid.close() + +# Create the array of PSD amplitude values. +psd_dBSPL_per_Hz = [] +frequencies_original = [] +for line in all_lines: + splitted_line = line.split() + psd_dBSPL_per_Hz.append(float(splitted_line[1])) + frequencies_original.append(float(splitted_line[0])) + +# Convert amplitudes in dBSPL/Hz into power in Pa^2/Hz. +psd_dBSPL_per_Hz = np.array(psd_dBSPL_per_Hz) +psd_Pa2_per_Hz = np.power(10, psd_dBSPL_per_Hz / 10) * 4e-10 + +# The TNR/PR operators require the frequency array to be regularly spaced. +# Thus, the original frequencies are interpolated to regularly spaced points. +frequencies_interp = np.linspace(0, 22050, len(frequencies_original)) +psd_Pa2_per_Hz_interp = np.interp(frequencies_interp, frequencies_original, psd_Pa2_per_Hz) + +# Create the input PSD field for computation of TNR and PR. +f_psd = fields_factory.create_scalar_field(num_entities=1, location=locations.time_freq) +f_psd.append(psd_Pa2_per_Hz_interp, 1) + +# Create and include a field containing the array of frequencies. +support = TimeFreqSupport() +f_frequencies = fields_factory.create_scalar_field(num_entities=1, location=locations.time_freq) +f_frequencies.append(frequencies_interp, 1) +support.time_frequencies = f_frequencies +f_psd.time_freq_support = support + +# %% +# Create a ``ToneToNoiseRatio`` object, set the created PSD field as input, and compute the TNR. +tone_to_noise_ratio = ToneToNoiseRatio(psd=f_psd) +tone_to_noise_ratio.process() + +# %% +# Print results. +number_tones = tone_to_noise_ratio.get_nb_tones() +TNR = tone_to_noise_ratio.get_max_TNR_value() +TNR_frequencies = tone_to_noise_ratio.get_peaks_frequencies() +TNR_values = tone_to_noise_ratio.get_TNR_values() +TNR_levels = tone_to_noise_ratio.get_peaks_levels() + +print( + f"\n" + f"Number of tones found: {number_tones}\n" + f"Maximum TNR value: {np.round(TNR, 1)} dB\n" + f"All detected peaks' frequencies (Hz): " + f"{np.round(TNR_frequencies)}\n" + f"All peaks' TNR values (dB): {np.round(TNR_values, 1)}\n" + f"All peaks' absolute levels (dB SPL): {np.round(TNR_levels, 1)}\n" +) + +# %% +# Plot the TNR over frequency. +tone_to_noise_ratio.plot() + +# %% +# Recalculate the TNR for specific frequencies. +frequencies_i = [261, 525, 786, 1836] +tone_to_noise_ratio = ToneToNoiseRatio(psd=f_psd, frequency_list=frequencies_i) +tone_to_noise_ratio.process() + +# %% +# Print information for a specific detected peak. +tone_to_noise_ratio_525 = tone_to_noise_ratio.get_single_tone_info(tone_index=1) +TNR_frequency = tone_to_noise_ratio_525[0] +TNR_width = tone_to_noise_ratio_525[4] - tone_to_noise_ratio_525[3] +TNR = tone_to_noise_ratio_525[1] +print( + f"\n" + f"TNR info for peak at ~525 Hz: \n" + f"Exact tone frequency: {round(TNR_frequency, 2)} Hz\n" + f"Tone width: {round(TNR_width, 2)} Hz\n" + f"TNR value: {round(TNR, 2)} dB\n\n" +) + +# %% +# Calculate PR from a PSD +# ~~~~~~~~~~~~~~~~~~~~~~~ +# Use the PowerSpectralDensity class to calculate a PSD, and compute Prominence Ratio (PR). + +# Load example data from WAV file. +path_flute_wav = download_flute_wav() +wav_loader = LoadWav(path_flute_wav) +wav_loader.process() +flute_signal = wav_loader.get_output()[0] + +# %% +# Create a PowerSpectralDensity object, set its input signal and parameters, and compute the PSD. +psd_object = PowerSpectralDensity( + flute_signal, fft_size=8192, window_type="HANN", window_length=8192, overlap=0.8 +) +psd_object.process() + +# %% +# Get the computed PSD as a Field. +f_psd = psd_object.get_output() + +# %% +# Create a ProminenceRatio object, set the computed PSD as input, and compute the PR. +prominence_ratio = ProminenceRatio(psd=f_psd) +prominence_ratio.process() + +# %% +# Print the results. +number_tones = prominence_ratio.get_nb_tones() +PR = prominence_ratio.get_max_PR_value() +PR_frequencies = prominence_ratio.get_peaks_frequencies() +PR_values = prominence_ratio.get_PR_values() +PR_levels = prominence_ratio.get_peaks_levels() +print( + f"\n" + f"Number of tones found: {number_tones}\n" + f"Maximum PR value: {np.round(PR, 1)} dB\n" + f"All detected peaks' frequencies (Hz): {np.round(PR_frequencies)}\n" + f"All peaks' PR values (dB): {np.round(PR_values, 1)}\n" + f"All peaks' absolute levels (dB SPL): {np.round(PR_levels, 1)}\n" +) + +# %% +# Plot the PR as a function of frequency. +prominence_ratio.plot() + +# %% +# Recalculate the PR for specific frequencies. +frequencies_i = [261, 525, 786, 1836] +prominence_ratio = ProminenceRatio(psd=f_psd, frequency_list=frequencies_i) +prominence_ratio.process() + +# %% +# Print information for a specific detected peak. +prominence_ratio_786 = prominence_ratio.get_single_tone_info(tone_index=2) +PR_frequency = prominence_ratio_786[0] +PR_width = prominence_ratio_786[4] - prominence_ratio_786[3] +PR = prominence_ratio_786[1] +print( + f"\n" + f"PR info for peak at ~786 Hz: \n" + f"Exact tone frequency: {round(PR_frequency, 2)} Hz\n" + f"Tone width: {round(PR_width, 2)} Hz\n" + f"PR value: {round(PR, 2)} dB\n" +) diff --git a/version/0.1/_downloads/bec8d65a221665d71d4c91e35b60e84d/001_initialize_server_and_deal_with_license.py b/version/0.1/_downloads/bec8d65a221665d71d4c91e35b60e84d/001_initialize_server_and_deal_with_license.py new file mode 100644 index 000000000..75f471395 --- /dev/null +++ b/version/0.1/_downloads/bec8d65a221665d71d4c91e35b60e84d/001_initialize_server_and_deal_with_license.py @@ -0,0 +1,150 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _initialize_server_and_deal_with_license: + +Initialize PyAnsys Sound and check out the license +-------------------------------------------------- + +This example shows how to initialize PyDPF-Core, load DPF Sound, and check out the +required Ansys license (``increment avrxp_snd_level1``) only once. It also shows +how to connect to the DPF server, verify where it is located, and get other useful +information. + +This example also demonstrates the use of the LicenseContextManager, a mechanism that lets you +check out the license only once for the duration of the session, which greatly improves performance. +It shows the execution time of a simple DPF Sound operator when you do not use the +LicenseContextManager versus when you do use it. + +**Prerequisites** + +Ensure that you have installed PyDPF-Core and DPF Sound according to procedures in +the PyDPF-Core documentation: + +- If you have installed the latest Ansys release, see `Install using pip + `_. +- If you want to use the DPF standalone version, see `Install DPF Server + `_. + + +""" + +# %% +# Perform required imports +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# Perform the required imports: + +# Load Ansys and other libraries. +import datetime + +import ansys.dpf.core as dpf + +from ansys.sound.core.examples_helpers import download_flute_wav +from ansys.sound.core.server_helpers import connect_to_or_start_server +from ansys.sound.core.signal_utilities import LoadWav + +# sphinx_gallery_start_ignore +# sphinx_gallery_thumbnail_path = '_static/_image/example004_thumbnail.png' +# sphinx_gallery_end_ignore + +# %% +# Use a DPF server without a LicenseContextManager +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Initialize a DPF server without using a LicenseContextManager. +# +# **Note**: When ``use_license_context=False``, the license is checked out each time +# you use a DPF Sound operator. + + +# Connect to a remote server or start a local server without using a LicenseContextManager +print("Connecting to the server without using a LicenseContextManager") +my_server = connect_to_or_start_server(use_license_context=False) + +# Check if you are using a local or remote server +has_local_server = dpf.server.has_local_server() +print(f"Local server: {has_local_server}") + +# If using a local server, display the path to the server +if has_local_server == True: + print(f"Local server path (server variable): {my_server.ansys_path}") + +# %% +# Display information about the server that you are using. +print(f"Server information: {my_server.info}") + +# %% +# Execute the PyAnsys Sound ``LoadWav`` operator several times in a row +# and measure the execution time. + +path_flute_wav = download_flute_wav() + +for i in range(5): + now = datetime.datetime.now() + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + fc_signal = wav_loader.get_output() + later = datetime.datetime.now() + execution_time = later - now + print( + f"Elapsed time (loop {i+1}): " + f"{execution_time.seconds + execution_time.microseconds/1e6}" + f" seconds" + ) + +# %% +# Disconnect (shut down) the server and release the license increment. +print("Disconnecting from the server and releasing the license increment.") +my_server = None + +# %% +# Use a DPF server with a LicenseContextManager +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Initialize a DPF server using a LicenseContextManager and execute the same code as run previously. +# +# **Note**: The LicenseContextManager is a mechanism that checks out a license increment +# when entering the context and releases it when exiting the context. + +# Connect to a remote server or start a local server using a LicenseContextManager +print("Connecting to the server using a LicenseContextManager") +my_server = connect_to_or_start_server(use_license_context=True) + +# Execute the same and measure the execution time +for i in range(5): + now = datetime.datetime.now() + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + fc_signal = wav_loader.get_output() + later = datetime.datetime.now() + execution_time = later - now + print( + f"Elapsed time (loop {i+1}): " + f"{execution_time.seconds + execution_time.microseconds / 1e6}" + f" seconds" + ) + +# %% +# You can see that the execution time is much faster when you use a LicenseContextManager. +# This is because when a LicenseContactManager is not used, the license is checked out +# each time you use a DPF Sound operator. diff --git a/version/0.1/_downloads/d4e7b50eb6faf0de68e82ee47851ae2f/001_initialize_server_and_deal_with_license.ipynb b/version/0.1/_downloads/d4e7b50eb6faf0de68e82ee47851ae2f/001_initialize_server_and_deal_with_license.ipynb new file mode 100644 index 000000000..d18683302 --- /dev/null +++ b/version/0.1/_downloads/d4e7b50eb6faf0de68e82ee47851ae2f/001_initialize_server_and_deal_with_license.ipynb @@ -0,0 +1,147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Initialize PyAnsys Sound and check out the license {#initialize_server_and_deal_with_license}\r\n\r\nThis example shows how to initialize PyDPF-Core, load DPF Sound, and\r\ncheck out the required Ansys license (`increment avrxp_snd_level1`) only\r\nonce. It also shows how to connect to the DPF server, verify where it is\r\nlocated, and get other useful information.\r\n\r\nThis example also demonstrates the use of the LicenseContextManager, a\r\nmechanism that lets you check out the license only once for the duration\r\nof the session, which greatly improves performance. It shows the\r\nexecution time of a simple DPF Sound operator when you do not use the\r\nLicenseContextManager versus when you do use it.\r\n\r\n**Prerequisites**\r\n\r\nEnsure that you have installed PyDPF-Core and DPF Sound according to\r\nprocedures in the PyDPF-Core documentation:\r\n\r\n- If you have installed the latest Ansys release, see [Install using\r\n pip](https://dpf.docs.pyansys.com/version/stable/getting_started/install.html#installation).\r\n- If you want to use the DPF standalone version, see [Install DPF\r\n Server](https://dpf.docs.pyansys.com/version/stable/getting_started/dpf_server.html#dpf-server).\r\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Perform required imports\r\n\r\nPerform the required imports:\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load Ansys and other libraries.\nimport datetime\n\nimport ansys.dpf.core as dpf\n\nfrom ansys.sound.core.examples_helpers import download_flute_wav\nfrom ansys.sound.core.server_helpers import connect_to_or_start_server\nfrom ansys.sound.core.signal_utilities import LoadWav" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Use a DPF server without a LicenseContextManager\r\n\r\nInitialize a DPF server without using a LicenseContextManager.\r\n\r\n**Note**: When `use_license_context=False`, the license is checked out\r\neach time you use a DPF Sound operator.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Connect to a remote server or start a local server without using a LicenseContextManager\nprint(\"Connecting to the server without using a LicenseContextManager\")\nmy_server = connect_to_or_start_server(use_license_context=False)\n\n# Check if you are using a local or remote server\nhas_local_server = dpf.server.has_local_server()\nprint(f\"Local server: {has_local_server}\")\n\n# If using a local server, display the path to the server\nif has_local_server == True:\n print(f\"Local server path (server variable): {my_server.ansys_path}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Display information about the server that you are using.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(f\"Server information: {my_server.info}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Execute the PyAnsys Sound `LoadWav` operator several times in a row and\r\nmeasure the execution time.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "path_flute_wav = download_flute_wav()\n\nfor i in range(5):\n now = datetime.datetime.now()\n wav_loader = LoadWav(path_flute_wav)\n wav_loader.process()\n fc_signal = wav_loader.get_output()\n later = datetime.datetime.now()\n execution_time = later - now\n print(\n f\"Elapsed time (loop {i+1}): \"\n f\"{execution_time.seconds + execution_time.microseconds/1e6}\"\n f\" seconds\"\n )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Disconnect (shut down) the server and release the license increment.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Disconnecting from the server and releasing the license increment.\")\nmy_server = None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Use a DPF server with a LicenseContextManager\r\n\r\nInitialize a DPF server using a LicenseContextManager and execute the\r\nsame code as run previously.\r\n\r\n**Note**: The LicenseContextManager is a mechanism that checks out a\r\nlicense increment when entering the context and releases it when exiting\r\nthe context.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Connect to a remote server or start a local server using a LicenseContextManager\nprint(\"Connecting to the server using a LicenseContextManager\")\nmy_server = connect_to_or_start_server(use_license_context=True)\n\n# Execute the same and measure the execution time\nfor i in range(5):\n now = datetime.datetime.now()\n wav_loader = LoadWav(path_flute_wav)\n wav_loader.process()\n fc_signal = wav_loader.get_output()\n later = datetime.datetime.now()\n execution_time = later - now\n print(\n f\"Elapsed time (loop {i+1}): \"\n f\"{execution_time.seconds + execution_time.microseconds / 1e6}\"\n f\" seconds\"\n )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can see that the execution time is much faster when you use a\r\nLicenseContextManager. This is because when a LicenseContactManager is\r\nnot used, the license is checked out each time you use a DPF Sound\r\noperator.\r\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/version/0.1/_downloads/ffc103c3ceb28ef76ec7d127b34f950e/003_compute_stft.py b/version/0.1/_downloads/ffc103c3ceb28ef76ec7d127b34f950e/003_compute_stft.py new file mode 100644 index 000000000..440b1bc0f --- /dev/null +++ b/version/0.1/_downloads/ffc103c3ceb28ef76ec7d127b34f950e/003_compute_stft.py @@ -0,0 +1,115 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _compute_stft_example: + +Compute the STFT and ISTFT +-------------------------- + +This example shows how to compute the short-time Fourier transform (STFT) of a signal. +It also shows how to compute the inverse short-time Fourier transform (ISTFT) from a +STFT matrix and get a signal. + +""" +# %% +# Set up analysis +# ~~~~~~~~~~~~~~~ +# Setting up the analysis consists of loading Ansys libraries, connecting to the +# DPF server, and retrieving the example files. + +# Load Ansys libraries. +from ansys.sound.core.examples_helpers import download_flute_wav +from ansys.sound.core.server_helpers import connect_to_or_start_server +from ansys.sound.core.signal_utilities import LoadWav +from ansys.sound.core.spectrogram_processing import Istft, Stft + +# Connect to a remote server or start a local server +my_server = connect_to_or_start_server(use_license_context=True) + +# %% +# Load a signal +# ~~~~~~~~~~~~~ +# Load a signal from a WAV file using the ``LoadWav`` class. It is returned as a DPF +# field container. For more information, see `fields_container +# `_ +# in the DPF-Core API documentation. + +# Return the input data of the example file +path_flute_wav = download_flute_wav() + +# Load the WAV file +wav_loader = LoadWav(path_flute_wav) +wav_loader.process() +fc_signal = wav_loader.get_output() + +# Plot the input signal +wav_loader.plot() + +# %% +# Compute and plot STFT +# ~~~~~~~~~~~~~~~~~~~~~ + +# Instantiate an instance of the ``Stft`` class using the previously loaded signal +# as an input. Use an FFT size of 1024 points and then display the STFT colormap. + +stft = Stft(fc_signal, fft_size=1024) + +# Process the STFT +stft.process() + +# Plot the output +stft.plot() + +# %% +# Modify the STFT parameters using the setters of the ``Stft`` class. +# Display the new STFT colormap. + +stft.fft_size = 4096 +stft.window_overlap = 0.95 +stft.window_type = "BARTLETT" + +# Reprocess the STFT with the new parameters +stft.process() + +# Plot the modified output +stft.plot() + +# %% +# Compute and plot ISTFT +# ~~~~~~~~~~~~~~~~~~~~~ + +# Obtain a time-domain signal using the ``Istft`` class. +# The input of the ``Istft`` class is the output STFT object previously computed. + +fc_stft = stft.get_output() + +# Instantiate the class +istft = Istft(fc_stft) + +# Process the ISTFT +istft.process() + +# %% +# Plot the output, which is the original signal. + +istft.plot() diff --git a/version/0.1/_downloads/ffdcb2941fe401ce86510cca5718724a/004_isolate_orders.ipynb b/version/0.1/_downloads/ffdcb2941fe401ce86510cca5718724a/004_isolate_orders.ipynb new file mode 100644 index 000000000..ad949c8e4 --- /dev/null +++ b/version/0.1/_downloads/ffdcb2941fe401ce86510cca5718724a/004_isolate_orders.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Isolate orders {#isolate_orders_example}\r\n\r\nOrders are harmonic and partial components in the sound related to the\r\nspeed of a rotating machine. This example shows how to isolate orders in\r\na signal containing an RPM profile. It also uses additional classes from\r\nPyAnsys Sound to compute spectrograms and the loudness of the isolated\r\nsignals.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Maximum frequency for STFT plots, change according to your need\nMAX_FREQUENCY_PLOT_STFT = 2000.0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set up analysis\r\n\r\nSetting up the analysis consists of loading Ansys libraries, connecting\r\nto the DPF server, and retrieving the example files.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Load Ansys libraries.\nimport os\nimport pathlib\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom ansys.sound.core.examples_helpers import (\n download_accel_with_rpm_2_wav,\n download_accel_with_rpm_3_wav,\n download_accel_with_rpm_wav,\n)\nfrom ansys.sound.core.psychoacoustics import LoudnessISO532_1_Stationary\nfrom ansys.sound.core.server_helpers import connect_to_or_start_server\nfrom ansys.sound.core.signal_utilities import LoadWav, WriteWav\nfrom ansys.sound.core.spectrogram_processing import IsolateOrders, Stft\n\n# Connect to a remote or start a local server\nmy_server = connect_to_or_start_server()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Define a custom function for STFT plots\r\n\r\nDefining a custom function for STFT plots lets you have more control\r\nover what you are displaying. While you could use the `Stft.plot()`\r\nmethod, the custom function defined here restricts the frequency range\r\nof the plot.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def plot_stft(stft_class, vmax):\n out = stft_class.get_output_as_nparray()\n\n # Extract first half of the STFT (second half is symmetrical)\n half_nfft = int(out.shape[0] / 2) + 1\n magnitude = stft_class.get_stft_magnitude_as_nparray()\n\n # Voluntarily ignore a numpy warning\n np.seterr(divide=\"ignore\")\n magnitude = 20 * np.log10(magnitude[0:half_nfft, :])\n np.seterr(divide=\"warn\")\n\n # Obtain sampling frequency, time steps, and number of time samples\n fs = 1.0 / (\n stft_class.signal.time_freq_support.time_frequencies.data[1]\n - stft_class.signal.time_freq_support.time_frequencies.data[0]\n )\n time_step = np.floor(stft_class.fft_size * (1.0 - stft_class.window_overlap) + 0.5) / fs\n num_time_index = len(stft_class.get_output().get_available_ids_for_label(\"time\"))\n\n # Define boundaries of the plot\n extent = [0, time_step * num_time_index, 0.0, fs / 2.0]\n\n # Plot\n plt.imshow(\n magnitude,\n origin=\"lower\",\n aspect=\"auto\",\n cmap=\"jet\",\n extent=extent,\n vmin=vmax - 70.0,\n vmax=vmax,\n )\n plt.colorbar(label=\"Amplitude (dB SPL)\")\n plt.ylabel(\"Frequency (Hz)\")\n plt.xlabel(\"Time (s)\")\n plt.ylim(\n [0.0, MAX_FREQUENCY_PLOT_STFT]\n ) # Change the value of MAX_FREQUENCY_PLOT_STFT if needed\n plt.title(\"STFT\")\n plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load a signal with an RPM profile\r\n\r\nLoad a signal that has been generated with Ansys Sound Analysis and\r\nSpecification (SAS) from a WAV file using the `LoadWav` class. This\r\nclass contains two channels:\r\n\r\n- The actual signal (an acceleration recording)\r\n- The associated RPM profile\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Return the input data of the example file\npath_accel_wav = download_accel_with_rpm_wav()\n\n# Load the WAV file.\nwav_loader = LoadWav(path_accel_wav)\nwav_loader.process()\nfc_signal = wav_loader.get_output()\n\n# Extract the audio signal and the RPM profile\nwav_signal, rpm_signal = wav_loader.get_output_as_nparray()\n\n# Extract time support associated with the signal\ntime_support = fc_signal[0].time_freq_support.time_frequencies.data\n\n# Plot the signal and its associated RPM profile\nfig, ax = plt.subplots(nrows=2, sharex=True)\nax[0].plot(time_support, wav_signal)\nax[0].set_title(\"Audio Signal\")\nax[0].set_ylabel(\"Amplitude (Pa)\")\nax[0].grid(True)\nax[1].plot(time_support, rpm_signal, color=\"red\")\nax[1].set_title(\"RPM profile\")\nax[1].set_ylabel(\"rpm\")\nax[1].grid(True)\nplt.xlabel(\"Time (s)\")\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot spectrogram of the original signal\r\n\r\nPlot the spectrogram of the original signal.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "stft = Stft(signal=fc_signal[0], window_overlap=0.9, fft_size=8192)\nstft.process()\nmax_stft = 20 * np.log10(np.max(stft.get_stft_magnitude_as_nparray()))\nplot_stft(stft, max_stft)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Isolate orders\r\n\r\nIsolate orders 2, 4, and 6 with the `IsolateOrders` class.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "field_wav, field_rpm = wav_loader.get_output()\n\n# Define parameters for order isolation\nfield_wav.unit = \"Pa\"\norder_to_isolate = [2, 4, 6] # Orders indexes to isolate as a list\nfft_size = 8192 # FFT Size (in samples)\nwindow_type = \"HANN\" # Window type\nwindow_overlap = 0.9 # Window overlap\nwidth_selection = 3 # Width of the order selection in Hz\n\n# Instantiate the ``IsolateOrders`` class with the parameters\nisolate_orders = IsolateOrders(\n signal=field_wav,\n rpm_profile=field_rpm,\n orders=order_to_isolate,\n fft_size=fft_size,\n window_type=window_type,\n window_overlap=window_overlap,\n width_selection=width_selection,\n)\n\n# Isolate orders\nisolate_orders.process()\n\n# Plot the spectrogram of the isolated orders\nstft.signal = isolate_orders.get_output()\nstft.process()\nplot_stft(stft, max_stft)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Isolate different orders\r\n\r\nChange FFT size, order indexes, and window type. Then re-isolate the\r\norders.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Change some parameters directly using the setters of the class\nisolate_orders.orders = [2, 6]\nisolate_orders.window_type = \"BLACKMAN\"\n\n# Reprocess (Must be called explicitly. Otherwise, the output won't be updated.)\nisolate_orders.process()\n\n\n# Plot the spectrogram of the isolated orders\nstft.signal = isolate_orders.get_output()\nstft.process()\nplot_stft(stft, max_stft)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Work with the isolated signal\r\n\r\nPlot the signal containing the isolated orders and compute its loudness.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Plot the signal directly using the method from the ``IsolateOrders`` class\nisolate_orders.plot()\n\n# Use the ``Loudness`` class to compute the loudness of the isolate signal\ninput_loudness = isolate_orders.get_output()\ninput_loudness.unit = \"Pa\"\nloudness = LoudnessISO532_1_Stationary(signal=input_loudness)\nloudness.process()\n\nloudness_isolated_signal = loudness.get_loudness_level_phon()\n\n# Compute the loudness for the original signal\nloudness.signal = field_wav\nloudness.process()\n\nloudness_original_signal = loudness.get_loudness_level_phon()\n\nprint(f\"Loudness of the original signal is {loudness_original_signal: .1f} phons.\")\nprint(f\"Loudness of the isolated signal is {loudness_isolated_signal: .1f} phons.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Isolate orders of several signals in a loop\r\n\r\nLoop over a list of given signals and write them as a WAV file.\r\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Obtain parent folder of the 'accel_with_rpm.wav' file\nparent_folder = pathlib.Path(path_accel_wav).parent.absolute()\n\npath_accel_wav_2 = download_accel_with_rpm_2_wav()\npath_accel_wav_3 = download_accel_with_rpm_3_wav()\npaths = (path_accel_wav, path_accel_wav_2, path_accel_wav_3)\n\nfft_sizes = [256, 2048, 4096]\n\nwav_writer = WriteWav()\n\n# Isolate orders for all the files containing RPM profiles in this folder\nfor file, fft_sz in zip(paths, fft_sizes):\n # Loading the file\n wav_loader.path_to_wav = file\n wav_loader.process()\n\n # Set parameters for order isolation\n isolate_orders.signal = wav_loader.get_output()[0]\n isolate_orders.rpm_profile = wav_loader.get_output()[1]\n isolate_orders.fft_size = fft_sz\n isolate_orders.process()\n\n # Write as a WAV file on the disk\n out_name = os.path.basename(file)[:-4] + \"_isolated_fft_size_\" + str(fft_sz) + \".wav\"\n path_to_write = parent_folder / out_name\n wav_writer.path_to_write = str(path_to_write)\n wav_writer.signal = isolate_orders.get_output()\n wav_writer.process()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/version/0.1/_images/sphx_glr_001_initialize_server_and_deal_with_license_thumb.png b/version/0.1/_images/sphx_glr_001_initialize_server_and_deal_with_license_thumb.png new file mode 100644 index 000000000..0af1af9f3 Binary files /dev/null and b/version/0.1/_images/sphx_glr_001_initialize_server_and_deal_with_license_thumb.png differ diff --git a/version/0.1/_images/sphx_glr_002_load_resample_amplify_write_wav_files_001.png b/version/0.1/_images/sphx_glr_002_load_resample_amplify_write_wav_files_001.png new file mode 100644 index 000000000..fc97af1e6 Binary files /dev/null and b/version/0.1/_images/sphx_glr_002_load_resample_amplify_write_wav_files_001.png differ diff --git a/version/0.1/_images/sphx_glr_002_load_resample_amplify_write_wav_files_thumb.png b/version/0.1/_images/sphx_glr_002_load_resample_amplify_write_wav_files_thumb.png new file mode 100644 index 000000000..e3b5c7815 Binary files /dev/null and b/version/0.1/_images/sphx_glr_002_load_resample_amplify_write_wav_files_thumb.png differ diff --git a/version/0.1/_images/sphx_glr_003_compute_stft_001.png b/version/0.1/_images/sphx_glr_003_compute_stft_001.png new file mode 100644 index 000000000..daf4a8522 Binary files /dev/null and b/version/0.1/_images/sphx_glr_003_compute_stft_001.png differ diff --git a/version/0.1/_images/sphx_glr_003_compute_stft_002.png b/version/0.1/_images/sphx_glr_003_compute_stft_002.png new file mode 100644 index 000000000..c6369c9c6 Binary files /dev/null and b/version/0.1/_images/sphx_glr_003_compute_stft_002.png differ diff --git a/version/0.1/_images/sphx_glr_003_compute_stft_003.png b/version/0.1/_images/sphx_glr_003_compute_stft_003.png new file mode 100644 index 000000000..07574738d Binary files /dev/null and b/version/0.1/_images/sphx_glr_003_compute_stft_003.png differ diff --git a/version/0.1/_images/sphx_glr_003_compute_stft_004.png b/version/0.1/_images/sphx_glr_003_compute_stft_004.png new file mode 100644 index 000000000..43b8a8f82 Binary files /dev/null and b/version/0.1/_images/sphx_glr_003_compute_stft_004.png differ diff --git a/version/0.1/_images/sphx_glr_003_compute_stft_thumb.png b/version/0.1/_images/sphx_glr_003_compute_stft_thumb.png new file mode 100644 index 000000000..8c9288e43 Binary files /dev/null and b/version/0.1/_images/sphx_glr_003_compute_stft_thumb.png differ diff --git a/version/0.1/_images/sphx_glr_004_isolate_orders_001.png b/version/0.1/_images/sphx_glr_004_isolate_orders_001.png new file mode 100644 index 000000000..92cfaad57 Binary files /dev/null and b/version/0.1/_images/sphx_glr_004_isolate_orders_001.png differ diff --git a/version/0.1/_images/sphx_glr_004_isolate_orders_002.png b/version/0.1/_images/sphx_glr_004_isolate_orders_002.png new file mode 100644 index 000000000..c1d68fdfb Binary files /dev/null and b/version/0.1/_images/sphx_glr_004_isolate_orders_002.png differ diff --git a/version/0.1/_images/sphx_glr_004_isolate_orders_003.png b/version/0.1/_images/sphx_glr_004_isolate_orders_003.png new file mode 100644 index 000000000..b3778df97 Binary files /dev/null and b/version/0.1/_images/sphx_glr_004_isolate_orders_003.png differ diff --git a/version/0.1/_images/sphx_glr_004_isolate_orders_004.png b/version/0.1/_images/sphx_glr_004_isolate_orders_004.png new file mode 100644 index 000000000..bb598fcf8 Binary files /dev/null and b/version/0.1/_images/sphx_glr_004_isolate_orders_004.png differ diff --git a/version/0.1/_images/sphx_glr_004_isolate_orders_005.png b/version/0.1/_images/sphx_glr_004_isolate_orders_005.png new file mode 100644 index 000000000..00f07658d Binary files /dev/null and b/version/0.1/_images/sphx_glr_004_isolate_orders_005.png differ diff --git a/version/0.1/_images/sphx_glr_004_isolate_orders_thumb.png b/version/0.1/_images/sphx_glr_004_isolate_orders_thumb.png new file mode 100644 index 000000000..68534350a Binary files /dev/null and b/version/0.1/_images/sphx_glr_004_isolate_orders_thumb.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_001.png b/version/0.1/_images/sphx_glr_005_xtract_feature_001.png new file mode 100644 index 000000000..09f1cd9a9 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_001.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_002.png b/version/0.1/_images/sphx_glr_005_xtract_feature_002.png new file mode 100644 index 000000000..6c5fbe9f3 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_002.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_003.png b/version/0.1/_images/sphx_glr_005_xtract_feature_003.png new file mode 100644 index 000000000..01cda0ce0 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_003.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_004.png b/version/0.1/_images/sphx_glr_005_xtract_feature_004.png new file mode 100644 index 000000000..88d941d0b Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_004.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_005.png b/version/0.1/_images/sphx_glr_005_xtract_feature_005.png new file mode 100644 index 000000000..5998762df Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_005.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_006.png b/version/0.1/_images/sphx_glr_005_xtract_feature_006.png new file mode 100644 index 000000000..88d941d0b Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_006.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_007.png b/version/0.1/_images/sphx_glr_005_xtract_feature_007.png new file mode 100644 index 000000000..3d05e985d Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_007.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_008.png b/version/0.1/_images/sphx_glr_005_xtract_feature_008.png new file mode 100644 index 000000000..bcea6affb Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_008.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_009.png b/version/0.1/_images/sphx_glr_005_xtract_feature_009.png new file mode 100644 index 000000000..9bee5d539 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_009.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_010.png b/version/0.1/_images/sphx_glr_005_xtract_feature_010.png new file mode 100644 index 000000000..976e49318 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_010.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_011.png b/version/0.1/_images/sphx_glr_005_xtract_feature_011.png new file mode 100644 index 000000000..a3d61f8c0 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_011.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_012.png b/version/0.1/_images/sphx_glr_005_xtract_feature_012.png new file mode 100644 index 000000000..b14ae9043 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_012.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_013.png b/version/0.1/_images/sphx_glr_005_xtract_feature_013.png new file mode 100644 index 000000000..e53acbfaa Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_013.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_014.png b/version/0.1/_images/sphx_glr_005_xtract_feature_014.png new file mode 100644 index 000000000..5d66393d7 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_014.png differ diff --git a/version/0.1/_images/sphx_glr_005_xtract_feature_thumb.png b/version/0.1/_images/sphx_glr_005_xtract_feature_thumb.png new file mode 100644 index 000000000..833142ab3 Binary files /dev/null and b/version/0.1/_images/sphx_glr_005_xtract_feature_thumb.png differ diff --git a/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_001.png b/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_001.png new file mode 100644 index 000000000..ff0300f3a Binary files /dev/null and b/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_001.png differ diff --git a/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_002.png b/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_002.png new file mode 100644 index 000000000..4c6247148 Binary files /dev/null and b/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_002.png differ diff --git a/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_thumb.png b/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_thumb.png new file mode 100644 index 000000000..c97eaa6a8 Binary files /dev/null and b/version/0.1/_images/sphx_glr_006_calculate_PR_and_TNR_thumb.png differ diff --git a/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_001.png b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_001.png new file mode 100644 index 000000000..a02ddc6db Binary files /dev/null and b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_001.png differ diff --git a/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_002.png b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_002.png new file mode 100644 index 000000000..93dd409f1 Binary files /dev/null and b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_002.png differ diff --git a/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_003.png b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_003.png new file mode 100644 index 000000000..729cf1dbe Binary files /dev/null and b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_003.png differ diff --git a/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_thumb.png b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_thumb.png new file mode 100644 index 000000000..57559c266 Binary files /dev/null and b/version/0.1/_images/sphx_glr_007_calculate_psychoacoustic_indicators_thumb.png differ diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..8edec64cf --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_frequencies.rst.txt new file mode 100644 index 000000000..cc105aaa6 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_bark\_band\_frequencies +============================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.get_bark_band_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_indexes.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_indexes.rst.txt new file mode 100644 index 000000000..7af0b9f62 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_bark_band_indexes.rst.txt @@ -0,0 +1,6 @@ +get\_bark\_band\_indexes +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.get_bark_band_indexes \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_fluctuation_strength.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_fluctuation_strength.rst.txt new file mode 100644 index 000000000..2c2a17e40 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_fluctuation_strength.rst.txt @@ -0,0 +1,6 @@ +get\_fluctuation\_strength +========================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.get_fluctuation_strength \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output.rst.txt new file mode 100644 index 000000000..6909de850 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..20eb7e0ba --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_specific_fluctuation_strength.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_specific_fluctuation_strength.rst.txt new file mode 100644 index 000000000..c7ca47a01 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.get_specific_fluctuation_strength.rst.txt @@ -0,0 +1,6 @@ +get\_specific\_fluctuation\_strength +==================================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.get_specific_fluctuation_strength \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.plot.rst.txt new file mode 100644 index 000000000..29213894a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.process.rst.txt new file mode 100644 index 000000000..71f378b07 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: FluctuationStrength.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.rst.txt new file mode 100644 index 000000000..968952f4b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.rst.txt @@ -0,0 +1,56 @@ +FluctuationStrength +=================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoclass:: FluctuationStrength + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + FluctuationStrength.convert_fields_container_to_np_array + + + FluctuationStrength.get_bark_band_frequencies + + + FluctuationStrength.get_bark_band_indexes + + + FluctuationStrength.get_fluctuation_strength + + + FluctuationStrength.get_output + + + FluctuationStrength.get_output_as_nparray + + + FluctuationStrength.get_specific_fluctuation_strength + + + FluctuationStrength.plot + + + FluctuationStrength.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + FluctuationStrength.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.signal.rst.txt new file mode 100644 index 000000000..9bc932468 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.FluctuationStrength.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: FluctuationStrength.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..a2fbd3ef4 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_frequencies.rst.txt new file mode 100644 index 000000000..b3b0f805a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_bark\_band\_frequencies +============================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.get_bark_band_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_indexes.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_indexes.rst.txt new file mode 100644 index 000000000..1369a7ce0 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_bark_band_indexes.rst.txt @@ -0,0 +1,6 @@ +get\_bark\_band\_indexes +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.get_bark_band_indexes \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_level_phon.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_level_phon.rst.txt new file mode 100644 index 000000000..7eba5cb51 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_level_phon.rst.txt @@ -0,0 +1,6 @@ +get\_loudness\_level\_phon +========================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.get_loudness_level_phon \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_sone.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_sone.rst.txt new file mode 100644 index 000000000..b43d44b38 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_loudness_sone.rst.txt @@ -0,0 +1,6 @@ +get\_loudness\_sone +=================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.get_loudness_sone \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output.rst.txt new file mode 100644 index 000000000..c13aca90d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..8dbbc3aed --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_specific_loudness.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_specific_loudness.rst.txt new file mode 100644 index 000000000..d6f75a8b5 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.get_specific_loudness.rst.txt @@ -0,0 +1,6 @@ +get\_specific\_loudness +======================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.get_specific_loudness \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.plot.rst.txt new file mode 100644 index 000000000..33fcb8bc1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.process.rst.txt new file mode 100644 index 000000000..9e915fe59 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_Stationary.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.rst.txt new file mode 100644 index 000000000..473773c86 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.rst.txt @@ -0,0 +1,59 @@ +LoudnessISO532\_1\_Stationary +============================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoclass:: LoudnessISO532_1_Stationary + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + LoudnessISO532_1_Stationary.convert_fields_container_to_np_array + + + LoudnessISO532_1_Stationary.get_bark_band_frequencies + + + LoudnessISO532_1_Stationary.get_bark_band_indexes + + + LoudnessISO532_1_Stationary.get_loudness_level_phon + + + LoudnessISO532_1_Stationary.get_loudness_sone + + + LoudnessISO532_1_Stationary.get_output + + + LoudnessISO532_1_Stationary.get_output_as_nparray + + + LoudnessISO532_1_Stationary.get_specific_loudness + + + LoudnessISO532_1_Stationary.plot + + + LoudnessISO532_1_Stationary.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + LoudnessISO532_1_Stationary.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.signal.rst.txt new file mode 100644 index 000000000..e26ed7ad0 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_Stationary.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: LoudnessISO532_1_Stationary.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..1f602b9ef --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L10_phon.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L10_phon.rst.txt new file mode 100644 index 000000000..d2f796799 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L10_phon.rst.txt @@ -0,0 +1,6 @@ +get\_L10\_phon +============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_L10_phon \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L5_phon.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L5_phon.rst.txt new file mode 100644 index 000000000..bc32dbc68 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_L5_phon.rst.txt @@ -0,0 +1,6 @@ +get\_L5\_phon +============= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_L5_phon \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N10_sone.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N10_sone.rst.txt new file mode 100644 index 000000000..d24b09d7e --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N10_sone.rst.txt @@ -0,0 +1,6 @@ +get\_N10\_sone +============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_N10_sone \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N5_sone.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N5_sone.rst.txt new file mode 100644 index 000000000..2da9bb425 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_N5_sone.rst.txt @@ -0,0 +1,6 @@ +get\_N5\_sone +============= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_N5_sone \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_level_phon_vs_time.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_level_phon_vs_time.rst.txt new file mode 100644 index 000000000..0ebd60e77 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_level_phon_vs_time.rst.txt @@ -0,0 +1,6 @@ +get\_loudness\_level\_phon\_vs\_time +==================================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_loudness_level_phon_vs_time \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_sone_vs_time.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_sone_vs_time.rst.txt new file mode 100644 index 000000000..d7ebd1a36 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_loudness_sone_vs_time.rst.txt @@ -0,0 +1,6 @@ +get\_loudness\_sone\_vs\_time +============================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_loudness_sone_vs_time \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output.rst.txt new file mode 100644 index 000000000..ba60208bc --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..0c5fd7278 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_time_scale.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_time_scale.rst.txt new file mode 100644 index 000000000..17bdb261b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.get_time_scale.rst.txt @@ -0,0 +1,6 @@ +get\_time\_scale +================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.get_time_scale \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.plot.rst.txt new file mode 100644 index 000000000..76381966f --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.process.rst.txt new file mode 100644 index 000000000..c5c4f89d8 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: LoudnessISO532_1_TimeVarying.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.rst.txt new file mode 100644 index 000000000..0e5477e8e --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.rst.txt @@ -0,0 +1,65 @@ +LoudnessISO532\_1\_TimeVarying +============================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoclass:: LoudnessISO532_1_TimeVarying + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + LoudnessISO532_1_TimeVarying.convert_fields_container_to_np_array + + + LoudnessISO532_1_TimeVarying.get_L10_phon + + + LoudnessISO532_1_TimeVarying.get_L5_phon + + + LoudnessISO532_1_TimeVarying.get_N10_sone + + + LoudnessISO532_1_TimeVarying.get_N5_sone + + + LoudnessISO532_1_TimeVarying.get_loudness_level_phon_vs_time + + + LoudnessISO532_1_TimeVarying.get_loudness_sone_vs_time + + + LoudnessISO532_1_TimeVarying.get_output + + + LoudnessISO532_1_TimeVarying.get_output_as_nparray + + + LoudnessISO532_1_TimeVarying.get_time_scale + + + LoudnessISO532_1_TimeVarying.plot + + + LoudnessISO532_1_TimeVarying.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + LoudnessISO532_1_TimeVarying.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.signal.rst.txt new file mode 100644 index 000000000..805f597ef --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.LoudnessISO532_1_TimeVarying.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: LoudnessISO532_1_TimeVarying.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..479d95eef --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.frequency_list.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.frequency_list.rst.txt new file mode 100644 index 000000000..9caa39fce --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.frequency_list.rst.txt @@ -0,0 +1,6 @@ +frequency\_list +=============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: ProminenceRatio.frequency_list \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_PR_values.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_PR_values.rst.txt new file mode 100644 index 000000000..e0047f782 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_PR_values.rst.txt @@ -0,0 +1,6 @@ +get\_PR\_values +=============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_PR_values \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_max_PR_value.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_max_PR_value.rst.txt new file mode 100644 index 000000000..6eff17a1a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_max_PR_value.rst.txt @@ -0,0 +1,6 @@ +get\_max\_PR\_value +=================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_max_PR_value \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_nb_tones.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_nb_tones.rst.txt new file mode 100644 index 000000000..8c735c7a7 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_nb_tones.rst.txt @@ -0,0 +1,6 @@ +get\_nb\_tones +============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_nb_tones \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output.rst.txt new file mode 100644 index 000000000..f6401be96 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..4bcd50d57 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_frequencies.rst.txt new file mode 100644 index 000000000..cac75237d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_frequencies +======================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_peaks_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_high_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_high_frequencies.rst.txt new file mode 100644 index 000000000..4a19e1223 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_high_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_high\_frequencies +============================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_peaks_high_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_levels.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_levels.rst.txt new file mode 100644 index 000000000..69b8a6b05 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_levels.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_levels +================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_peaks_levels \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_low_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_low_frequencies.rst.txt new file mode 100644 index 000000000..cacfaa924 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_peaks_low_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_low\_frequencies +============================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_peaks_low_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_reference_curve.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_reference_curve.rst.txt new file mode 100644 index 000000000..543beddde --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_reference_curve.rst.txt @@ -0,0 +1,6 @@ +get\_reference\_curve +===================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_reference_curve \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_single_tone_info.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_single_tone_info.rst.txt new file mode 100644 index 000000000..0c2ac0eac --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.get_single_tone_info.rst.txt @@ -0,0 +1,6 @@ +get\_single\_tone\_info +======================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.get_single_tone_info \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.plot.rst.txt new file mode 100644 index 000000000..466161d82 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.process.rst.txt new file mode 100644 index 000000000..d4d0743de --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ProminenceRatio.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.psd.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.psd.rst.txt new file mode 100644 index 000000000..06d0e06f5 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.psd.rst.txt @@ -0,0 +1,6 @@ +psd +=== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: ProminenceRatio.psd \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.rst.txt new file mode 100644 index 000000000..a942dd08c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ProminenceRatio.rst.txt @@ -0,0 +1,72 @@ +ProminenceRatio +=============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoclass:: ProminenceRatio + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + ProminenceRatio.convert_fields_container_to_np_array + + + ProminenceRatio.get_PR_values + + + ProminenceRatio.get_max_PR_value + + + ProminenceRatio.get_nb_tones + + + ProminenceRatio.get_output + + + ProminenceRatio.get_output_as_nparray + + + ProminenceRatio.get_peaks_frequencies + + + ProminenceRatio.get_peaks_high_frequencies + + + ProminenceRatio.get_peaks_levels + + + ProminenceRatio.get_peaks_low_frequencies + + + ProminenceRatio.get_reference_curve + + + ProminenceRatio.get_single_tone_info + + + ProminenceRatio.plot + + + ProminenceRatio.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + ProminenceRatio.frequency_list + ProminenceRatio.psd + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..9e8b517b0 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_frequencies.rst.txt new file mode 100644 index 000000000..c8e81ce06 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_bark\_band\_frequencies +============================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.get_bark_band_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_indexes.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_indexes.rst.txt new file mode 100644 index 000000000..5694fbb78 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_bark_band_indexes.rst.txt @@ -0,0 +1,6 @@ +get\_bark\_band\_indexes +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.get_bark_band_indexes \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output.rst.txt new file mode 100644 index 000000000..c6736ac65 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..25a747d9d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_roughness.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_roughness.rst.txt new file mode 100644 index 000000000..bc0b258e5 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_roughness.rst.txt @@ -0,0 +1,6 @@ +get\_roughness +============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.get_roughness \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_specific_roughness.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_specific_roughness.rst.txt new file mode 100644 index 000000000..0f740e6e0 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.get_specific_roughness.rst.txt @@ -0,0 +1,6 @@ +get\_specific\_roughness +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.get_specific_roughness \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.plot.rst.txt new file mode 100644 index 000000000..39d9ec784 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.process.rst.txt new file mode 100644 index 000000000..df63f2676 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Roughness.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.rst.txt new file mode 100644 index 000000000..21f1a6428 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.rst.txt @@ -0,0 +1,56 @@ +Roughness +========= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoclass:: Roughness + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + Roughness.convert_fields_container_to_np_array + + + Roughness.get_bark_band_frequencies + + + Roughness.get_bark_band_indexes + + + Roughness.get_output + + + Roughness.get_output_as_nparray + + + Roughness.get_roughness + + + Roughness.get_specific_roughness + + + Roughness.plot + + + Roughness.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + Roughness.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.signal.rst.txt new file mode 100644 index 000000000..3f464d19c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Roughness.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: Roughness.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..754593bbf --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Sharpness.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output.rst.txt new file mode 100644 index 000000000..be0f66535 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Sharpness.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..63826f370 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Sharpness.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_sharpness.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_sharpness.rst.txt new file mode 100644 index 000000000..4961355c2 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.get_sharpness.rst.txt @@ -0,0 +1,6 @@ +get\_sharpness +============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Sharpness.get_sharpness \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.plot.rst.txt new file mode 100644 index 000000000..45b2882ff --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Sharpness.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.process.rst.txt new file mode 100644 index 000000000..f5c20d6b4 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: Sharpness.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.rst.txt new file mode 100644 index 000000000..c187a61f5 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.rst.txt @@ -0,0 +1,47 @@ +Sharpness +========= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoclass:: Sharpness + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + Sharpness.convert_fields_container_to_np_array + + + Sharpness.get_output + + + Sharpness.get_output_as_nparray + + + Sharpness.get_sharpness + + + Sharpness.plot + + + Sharpness.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + Sharpness.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.signal.rst.txt new file mode 100644 index 000000000..755b79cf1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.Sharpness.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: Sharpness.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..c777026e3 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.frequency_list.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.frequency_list.rst.txt new file mode 100644 index 000000000..a2a3a1918 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.frequency_list.rst.txt @@ -0,0 +1,6 @@ +frequency\_list +=============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: ToneToNoiseRatio.frequency_list \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_TNR_values.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_TNR_values.rst.txt new file mode 100644 index 000000000..6b67f0902 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_TNR_values.rst.txt @@ -0,0 +1,6 @@ +get\_TNR\_values +================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_TNR_values \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_max_TNR_value.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_max_TNR_value.rst.txt new file mode 100644 index 000000000..5c40f087d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_max_TNR_value.rst.txt @@ -0,0 +1,6 @@ +get\_max\_TNR\_value +==================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_max_TNR_value \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_nb_tones.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_nb_tones.rst.txt new file mode 100644 index 000000000..248386b79 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_nb_tones.rst.txt @@ -0,0 +1,6 @@ +get\_nb\_tones +============== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_nb_tones \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output.rst.txt new file mode 100644 index 000000000..3cfc18dc1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..a5dc0dae4 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_frequencies.rst.txt new file mode 100644 index 000000000..0e7e5edbc --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_frequencies +======================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_peaks_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_high_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_high_frequencies.rst.txt new file mode 100644 index 000000000..8a599872a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_high_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_high\_frequencies +============================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_peaks_high_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_levels.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_levels.rst.txt new file mode 100644 index 000000000..410196714 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_levels.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_levels +================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_peaks_levels \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_low_frequencies.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_low_frequencies.rst.txt new file mode 100644 index 000000000..0871c53dd --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_peaks_low_frequencies.rst.txt @@ -0,0 +1,6 @@ +get\_peaks\_low\_frequencies +============================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_peaks_low_frequencies \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_reference_curve.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_reference_curve.rst.txt new file mode 100644 index 000000000..5b939973a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_reference_curve.rst.txt @@ -0,0 +1,6 @@ +get\_reference\_curve +===================== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_reference_curve \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_single_tone_info.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_single_tone_info.rst.txt new file mode 100644 index 000000000..4f5b21b12 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.get_single_tone_info.rst.txt @@ -0,0 +1,6 @@ +get\_single\_tone\_info +======================= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.get_single_tone_info \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.plot.rst.txt new file mode 100644 index 000000000..637d331df --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.process.rst.txt new file mode 100644 index 000000000..ae011056e --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. automethod:: ToneToNoiseRatio.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.psd.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.psd.rst.txt new file mode 100644 index 000000000..b3db45518 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.psd.rst.txt @@ -0,0 +1,6 @@ +psd +=== + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoproperty:: ToneToNoiseRatio.psd \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.rst.txt new file mode 100644 index 000000000..2b4928bf1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.psychoacoustics.ToneToNoiseRatio.rst.txt @@ -0,0 +1,72 @@ +ToneToNoiseRatio +================ + +.. currentmodule:: ansys.sound.core.psychoacoustics + +.. autoclass:: ToneToNoiseRatio + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + ToneToNoiseRatio.convert_fields_container_to_np_array + + + ToneToNoiseRatio.get_TNR_values + + + ToneToNoiseRatio.get_max_TNR_value + + + ToneToNoiseRatio.get_nb_tones + + + ToneToNoiseRatio.get_output + + + ToneToNoiseRatio.get_output_as_nparray + + + ToneToNoiseRatio.get_peaks_frequencies + + + ToneToNoiseRatio.get_peaks_high_frequencies + + + ToneToNoiseRatio.get_peaks_levels + + + ToneToNoiseRatio.get_peaks_low_frequencies + + + ToneToNoiseRatio.get_reference_curve + + + ToneToNoiseRatio.get_single_tone_info + + + ToneToNoiseRatio.plot + + + ToneToNoiseRatio.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + ToneToNoiseRatio.frequency_list + ToneToNoiseRatio.psd + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.server_helpers._connect_to_or_start_server.connect_to_or_start_server.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.server_helpers._connect_to_or_start_server.connect_to_or_start_server.rst.txt new file mode 100644 index 000000000..a4d40a7ea --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.server_helpers._connect_to_or_start_server.connect_to_or_start_server.rst.txt @@ -0,0 +1,6 @@ +connect\_to\_or\_start\_server +============================== + +.. currentmodule:: ansys.sound.core.server_helpers._connect_to_or_start_server + +.. autofunction:: connect_to_or_start_server \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.server_helpers._validate_dpf_sound_connection.validate_dpf_sound_connection.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.server_helpers._validate_dpf_sound_connection.validate_dpf_sound_connection.rst.txt new file mode 100644 index 000000000..02339ce55 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.server_helpers._validate_dpf_sound_connection.validate_dpf_sound_connection.rst.txt @@ -0,0 +1,6 @@ +validate\_dpf\_sound\_connection +================================ + +.. currentmodule:: ansys.sound.core.server_helpers._validate_dpf_sound_connection + +.. autofunction:: validate_dpf_sound_connection \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..e1063805d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ApplyGain.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain.rst.txt new file mode 100644 index 000000000..4bcd2b5c3 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain.rst.txt @@ -0,0 +1,6 @@ +gain +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: ApplyGain.gain \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain_in_db.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain_in_db.rst.txt new file mode 100644 index 000000000..c450e8ada --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.gain_in_db.rst.txt @@ -0,0 +1,6 @@ +gain\_in\_db +============ + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: ApplyGain.gain_in_db \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output.rst.txt new file mode 100644 index 000000000..df9c25cf5 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ApplyGain.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..05e5d3019 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ApplyGain.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.plot.rst.txt new file mode 100644 index 000000000..146f95065 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ApplyGain.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.process.rst.txt new file mode 100644 index 000000000..7bc1dd6d7 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ApplyGain.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.rst.txt new file mode 100644 index 000000000..cb316f87f --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.rst.txt @@ -0,0 +1,46 @@ +ApplyGain +========= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: ApplyGain + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + ApplyGain.convert_fields_container_to_np_array + + + ApplyGain.get_output + + + ApplyGain.get_output_as_nparray + + + ApplyGain.plot + + + ApplyGain.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + ApplyGain.gain + ApplyGain.gain_in_db + ApplyGain.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.signal.rst.txt new file mode 100644 index 000000000..d1d129181 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ApplyGain.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: ApplyGain.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..217dc0601 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CreateSoundField.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.data.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.data.rst.txt new file mode 100644 index 000000000..15d30d562 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.data.rst.txt @@ -0,0 +1,6 @@ +data +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: CreateSoundField.data \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output.rst.txt new file mode 100644 index 000000000..3f2b63a06 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CreateSoundField.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..940fc8f53 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CreateSoundField.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.plot.rst.txt new file mode 100644 index 000000000..fa9fd0587 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CreateSoundField.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.process.rst.txt new file mode 100644 index 000000000..e92d1d663 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CreateSoundField.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.rst.txt new file mode 100644 index 000000000..69378fc02 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.rst.txt @@ -0,0 +1,46 @@ +CreateSoundField +================ + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: CreateSoundField + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + CreateSoundField.convert_fields_container_to_np_array + + + CreateSoundField.get_output + + + CreateSoundField.get_output_as_nparray + + + CreateSoundField.plot + + + CreateSoundField.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + CreateSoundField.data + CreateSoundField.sampling_frequency + CreateSoundField.unit + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.sampling_frequency.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.sampling_frequency.rst.txt new file mode 100644 index 000000000..8d785e9d4 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.sampling_frequency.rst.txt @@ -0,0 +1,6 @@ +sampling\_frequency +=================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: CreateSoundField.sampling_frequency \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.unit.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.unit.rst.txt new file mode 100644 index 000000000..b7539d659 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CreateSoundField.unit.rst.txt @@ -0,0 +1,6 @@ +unit +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: CreateSoundField.unit \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..075e0d672 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CropSignal.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.end_time.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.end_time.rst.txt new file mode 100644 index 000000000..5245a294e --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.end_time.rst.txt @@ -0,0 +1,6 @@ +end\_time +========= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: CropSignal.end_time \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output.rst.txt new file mode 100644 index 000000000..8dc819857 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CropSignal.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..2eadf1a08 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CropSignal.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.plot.rst.txt new file mode 100644 index 000000000..905dbec3a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CropSignal.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.process.rst.txt new file mode 100644 index 000000000..966a085f4 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: CropSignal.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.rst.txt new file mode 100644 index 000000000..c3619ab09 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.rst.txt @@ -0,0 +1,46 @@ +CropSignal +========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: CropSignal + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + CropSignal.convert_fields_container_to_np_array + + + CropSignal.get_output + + + CropSignal.get_output_as_nparray + + + CropSignal.plot + + + CropSignal.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + CropSignal.end_time + CropSignal.signal + CropSignal.start_time + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.signal.rst.txt new file mode 100644 index 000000000..89116a9ff --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: CropSignal.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.start_time.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.start_time.rst.txt new file mode 100644 index 000000000..4a2007f1b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.CropSignal.start_time.rst.txt @@ -0,0 +1,6 @@ +start\_time +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: CropSignal.start_time \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..a64209422 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: LoadWav.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output.rst.txt new file mode 100644 index 000000000..0d78fce2b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: LoadWav.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..b83062d95 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: LoadWav.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.path_to_wav.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.path_to_wav.rst.txt new file mode 100644 index 000000000..37d4bf1bf --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.path_to_wav.rst.txt @@ -0,0 +1,6 @@ +path\_to\_wav +============= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: LoadWav.path_to_wav \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.plot.rst.txt new file mode 100644 index 000000000..60183483d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: LoadWav.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.process.rst.txt new file mode 100644 index 000000000..2d7ecca73 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: LoadWav.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.rst.txt new file mode 100644 index 000000000..4db060f96 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.LoadWav.rst.txt @@ -0,0 +1,44 @@ +LoadWav +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: LoadWav + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + LoadWav.convert_fields_container_to_np_array + + + LoadWav.get_output + + + LoadWav.get_output_as_nparray + + + LoadWav.plot + + + LoadWav.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + LoadWav.path_to_wav + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..e3f4ba15c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: Resample.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output.rst.txt new file mode 100644 index 000000000..9fd8c0c60 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: Resample.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..67624a45c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: Resample.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.new_sampling_frequency.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.new_sampling_frequency.rst.txt new file mode 100644 index 000000000..c61a46a92 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.new_sampling_frequency.rst.txt @@ -0,0 +1,6 @@ +new\_sampling\_frequency +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: Resample.new_sampling_frequency \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.plot.rst.txt new file mode 100644 index 000000000..462edd073 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: Resample.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.process.rst.txt new file mode 100644 index 000000000..29c3e6e7e --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: Resample.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.rst.txt new file mode 100644 index 000000000..edcf7ac24 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.rst.txt @@ -0,0 +1,45 @@ +Resample +======== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: Resample + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + Resample.convert_fields_container_to_np_array + + + Resample.get_output + + + Resample.get_output_as_nparray + + + Resample.plot + + + Resample.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + Resample.new_sampling_frequency + Resample.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.signal.rst.txt new file mode 100644 index 000000000..2324c4cc2 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.Resample.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: Resample.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..04f628edf --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: SumSignals.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output.rst.txt new file mode 100644 index 000000000..b8ee449cb --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: SumSignals.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..928dfc40c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: SumSignals.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.plot.rst.txt new file mode 100644 index 000000000..2ef91e389 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: SumSignals.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.process.rst.txt new file mode 100644 index 000000000..7ecb0bda1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: SumSignals.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.rst.txt new file mode 100644 index 000000000..107a22f7d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.rst.txt @@ -0,0 +1,44 @@ +SumSignals +========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: SumSignals + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + SumSignals.convert_fields_container_to_np_array + + + SumSignals.get_output + + + SumSignals.get_output_as_nparray + + + SumSignals.plot + + + SumSignals.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + SumSignals.signals + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.signals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.signals.rst.txt new file mode 100644 index 000000000..2f73d45d7 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.SumSignals.signals.rst.txt @@ -0,0 +1,6 @@ +signals +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: SumSignals.signals \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.bit_depth.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.bit_depth.rst.txt new file mode 100644 index 000000000..9d8079c34 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.bit_depth.rst.txt @@ -0,0 +1,6 @@ +bit\_depth +========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: WriteWav.bit_depth \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..73205ac15 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: WriteWav.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output.rst.txt new file mode 100644 index 000000000..c250c9c3d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: WriteWav.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..3ac277f57 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: WriteWav.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.path_to_write.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.path_to_write.rst.txt new file mode 100644 index 000000000..c4e8328bf --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.path_to_write.rst.txt @@ -0,0 +1,6 @@ +path\_to\_write +=============== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: WriteWav.path_to_write \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.plot.rst.txt new file mode 100644 index 000000000..6cf8058a9 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: WriteWav.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.process.rst.txt new file mode 100644 index 000000000..3b1facc63 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: WriteWav.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.rst.txt new file mode 100644 index 000000000..0ef1cafc1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.rst.txt @@ -0,0 +1,46 @@ +WriteWav +======== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: WriteWav + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + WriteWav.convert_fields_container_to_np_array + + + WriteWav.get_output + + + WriteWav.get_output_as_nparray + + + WriteWav.plot + + + WriteWav.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + WriteWav.bit_depth + WriteWav.path_to_write + WriteWav.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.signal.rst.txt new file mode 100644 index 000000000..763880a41 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.WriteWav.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: WriteWav.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..0a64643be --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ZeroPad.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.duration_zeros.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.duration_zeros.rst.txt new file mode 100644 index 000000000..1a7e512db --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.duration_zeros.rst.txt @@ -0,0 +1,6 @@ +duration\_zeros +=============== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: ZeroPad.duration_zeros \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output.rst.txt new file mode 100644 index 000000000..87797dadf --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ZeroPad.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..ba035bfa7 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ZeroPad.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.plot.rst.txt new file mode 100644 index 000000000..6c0992ece --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ZeroPad.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.process.rst.txt new file mode 100644 index 000000000..b4cc0dadb --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. automethod:: ZeroPad.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.rst.txt new file mode 100644 index 000000000..e8c168a06 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.rst.txt @@ -0,0 +1,45 @@ +ZeroPad +======= + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoclass:: ZeroPad + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + ZeroPad.convert_fields_container_to_np_array + + + ZeroPad.get_output + + + ZeroPad.get_output_as_nparray + + + ZeroPad.plot + + + ZeroPad.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + ZeroPad.duration_zeros + ZeroPad.signal + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.signal.rst.txt new file mode 100644 index 000000000..06b60ca54 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.signal_utilities.ZeroPad.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.signal_utilities + +.. autoproperty:: ZeroPad.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..2598a336c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: IsolateOrders.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.fft_size.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.fft_size.rst.txt new file mode 100644 index 000000000..bf178b469 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.fft_size.rst.txt @@ -0,0 +1,6 @@ +fft\_size +========= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: IsolateOrders.fft_size \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output.rst.txt new file mode 100644 index 000000000..99649327c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: IsolateOrders.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..84b711eb3 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: IsolateOrders.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.orders.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.orders.rst.txt new file mode 100644 index 000000000..7fa8f96c0 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.orders.rst.txt @@ -0,0 +1,6 @@ +orders +====== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: IsolateOrders.orders \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.plot.rst.txt new file mode 100644 index 000000000..89253af1b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: IsolateOrders.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.process.rst.txt new file mode 100644 index 000000000..756dc1855 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: IsolateOrders.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.rpm_profile.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.rpm_profile.rst.txt new file mode 100644 index 000000000..76c8d6826 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.rpm_profile.rst.txt @@ -0,0 +1,6 @@ +rpm\_profile +============ + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: IsolateOrders.rpm_profile \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.rst.txt new file mode 100644 index 000000000..ce31a9f2e --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.rst.txt @@ -0,0 +1,50 @@ +IsolateOrders +============= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoclass:: IsolateOrders + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + IsolateOrders.convert_fields_container_to_np_array + + + IsolateOrders.get_output + + + IsolateOrders.get_output_as_nparray + + + IsolateOrders.plot + + + IsolateOrders.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + IsolateOrders.fft_size + IsolateOrders.orders + IsolateOrders.rpm_profile + IsolateOrders.signal + IsolateOrders.width_selection + IsolateOrders.window_overlap + IsolateOrders.window_type + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.signal.rst.txt new file mode 100644 index 000000000..cce4a434c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: IsolateOrders.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.width_selection.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.width_selection.rst.txt new file mode 100644 index 000000000..b327a28ac --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.width_selection.rst.txt @@ -0,0 +1,6 @@ +width\_selection +================ + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: IsolateOrders.width_selection \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_overlap.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_overlap.rst.txt new file mode 100644 index 000000000..e2399cb95 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_overlap.rst.txt @@ -0,0 +1,6 @@ +window\_overlap +=============== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: IsolateOrders.window_overlap \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_type.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_type.rst.txt new file mode 100644 index 000000000..bc154d3f7 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.IsolateOrders.window_type.rst.txt @@ -0,0 +1,6 @@ +window\_type +============ + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: IsolateOrders.window_type \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..92fb0b437 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Istft.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output.rst.txt new file mode 100644 index 000000000..df04ae543 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Istft.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..be12b5c75 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Istft.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.plot.rst.txt new file mode 100644 index 000000000..9a0059801 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Istft.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.process.rst.txt new file mode 100644 index 000000000..a753e6530 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Istft.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.rst.txt new file mode 100644 index 000000000..c16346539 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.rst.txt @@ -0,0 +1,44 @@ +Istft +===== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoclass:: Istft + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + Istft.convert_fields_container_to_np_array + + + Istft.get_output + + + Istft.get_output_as_nparray + + + Istft.plot + + + Istft.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + Istft.stft + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.stft.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.stft.rst.txt new file mode 100644 index 000000000..d2ee62406 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Istft.stft.rst.txt @@ -0,0 +1,6 @@ +stft +==== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: Istft.stft \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..1ba2ba947 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Stft.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.fft_size.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.fft_size.rst.txt new file mode 100644 index 000000000..d3ab5b4cc --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.fft_size.rst.txt @@ -0,0 +1,6 @@ +fft\_size +========= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: Stft.fft_size \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output.rst.txt new file mode 100644 index 000000000..d09e2efe1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Stft.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..4fe8bc139 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Stft.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_magnitude_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_magnitude_as_nparray.rst.txt new file mode 100644 index 000000000..014db16b3 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_magnitude_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_stft\_magnitude\_as\_nparray +================================= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Stft.get_stft_magnitude_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_phase_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_phase_as_nparray.rst.txt new file mode 100644 index 000000000..342d61c04 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.get_stft_phase_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_stft\_phase\_as\_nparray +============================= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Stft.get_stft_phase_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.plot.rst.txt new file mode 100644 index 000000000..e2f09b089 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Stft.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.process.rst.txt new file mode 100644 index 000000000..6eacc285b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. automethod:: Stft.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.rst.txt new file mode 100644 index 000000000..6cc7334ef --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.rst.txt @@ -0,0 +1,53 @@ +Stft +==== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoclass:: Stft + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + Stft.convert_fields_container_to_np_array + + + Stft.get_output + + + Stft.get_output_as_nparray + + + Stft.get_stft_magnitude_as_nparray + + + Stft.get_stft_phase_as_nparray + + + Stft.plot + + + Stft.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + Stft.fft_size + Stft.signal + Stft.window_overlap + Stft.window_type + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.signal.rst.txt new file mode 100644 index 000000000..d72c37990 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.signal.rst.txt @@ -0,0 +1,6 @@ +signal +====== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: Stft.signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_overlap.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_overlap.rst.txt new file mode 100644 index 000000000..d4cd56f14 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_overlap.rst.txt @@ -0,0 +1,6 @@ +window\_overlap +=============== + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: Stft.window_overlap \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_type.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_type.rst.txt new file mode 100644 index 000000000..c15aeeafb --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.spectrogram_processing.Stft.window_type.rst.txt @@ -0,0 +1,6 @@ +window\_type +============ + +.. currentmodule:: ansys.sound.core.spectrogram_processing + +.. autoproperty:: Stft.window_type \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..593e5501c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: Xtract.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output.rst.txt new file mode 100644 index 000000000..1c3866185 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: Xtract.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..2753a7295 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: Xtract.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.input_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.input_signal.rst.txt new file mode 100644 index 000000000..4661aeadf --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.input_signal.rst.txt @@ -0,0 +1,6 @@ +input\_signal +============= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.input_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_noise_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_noise_signal.rst.txt new file mode 100644 index 000000000..bb003f457 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_noise_signal.rst.txt @@ -0,0 +1,6 @@ +output\_noise\_signal +===================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.output_noise_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_remainder_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_remainder_signal.rst.txt new file mode 100644 index 000000000..a0c855fa1 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_remainder_signal.rst.txt @@ -0,0 +1,6 @@ +output\_remainder\_signal +========================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.output_remainder_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_tonal_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_tonal_signal.rst.txt new file mode 100644 index 000000000..f9f1cdfe3 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_tonal_signal.rst.txt @@ -0,0 +1,6 @@ +output\_tonal\_signal +===================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.output_tonal_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_transient_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_transient_signal.rst.txt new file mode 100644 index 000000000..76268ca49 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.output_transient_signal.rst.txt @@ -0,0 +1,6 @@ +output\_transient\_signal +========================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.output_transient_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_denoiser.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_denoiser.rst.txt new file mode 100644 index 000000000..f0114dc5a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_denoiser.rst.txt @@ -0,0 +1,6 @@ +parameters\_denoiser +==================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.parameters_denoiser \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_tonal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_tonal.rst.txt new file mode 100644 index 000000000..663308af8 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_tonal.rst.txt @@ -0,0 +1,6 @@ +parameters\_tonal +================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.parameters_tonal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_transient.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_transient.rst.txt new file mode 100644 index 000000000..23405c748 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.parameters_transient.rst.txt @@ -0,0 +1,6 @@ +parameters\_transient +===================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: Xtract.parameters_transient \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.plot.rst.txt new file mode 100644 index 000000000..073616df7 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: Xtract.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.process.rst.txt new file mode 100644 index 000000000..e21809eb6 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: Xtract.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.rst.txt new file mode 100644 index 000000000..e1dfc2220 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.Xtract.rst.txt @@ -0,0 +1,51 @@ +Xtract +====== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoclass:: Xtract + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + Xtract.convert_fields_container_to_np_array + + + Xtract.get_output + + + Xtract.get_output_as_nparray + + + Xtract.plot + + + Xtract.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + Xtract.input_signal + Xtract.output_noise_signal + Xtract.output_remainder_signal + Xtract.output_tonal_signal + Xtract.output_transient_signal + Xtract.parameters_denoiser + Xtract.parameters_tonal + Xtract.parameters_transient + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..4aad7202b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiser.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output.rst.txt new file mode 100644 index 000000000..09790a775 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiser.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..9951df79b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiser.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_parameters.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_parameters.rst.txt new file mode 100644 index 000000000..83182e102 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_parameters.rst.txt @@ -0,0 +1,6 @@ +input\_parameters +================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractDenoiser.input_parameters \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_signal.rst.txt new file mode 100644 index 000000000..4f468d55a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.input_signal.rst.txt @@ -0,0 +1,6 @@ +input\_signal +============= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractDenoiser.input_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_denoised_signals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_denoised_signals.rst.txt new file mode 100644 index 000000000..490524e99 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_denoised_signals.rst.txt @@ -0,0 +1,6 @@ +output\_denoised\_signals +========================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractDenoiser.output_denoised_signals \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_noise_signals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_noise_signals.rst.txt new file mode 100644 index 000000000..f3e2d164d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.output_noise_signals.rst.txt @@ -0,0 +1,6 @@ +output\_noise\_signals +====================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractDenoiser.output_noise_signals \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.plot.rst.txt new file mode 100644 index 000000000..788d1f1ce --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiser.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.process.rst.txt new file mode 100644 index 000000000..aea6a80f6 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiser.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.rst.txt new file mode 100644 index 000000000..f8d28f766 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiser.rst.txt @@ -0,0 +1,47 @@ +XtractDenoiser +============== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoclass:: XtractDenoiser + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + XtractDenoiser.convert_fields_container_to_np_array + + + XtractDenoiser.get_output + + + XtractDenoiser.get_output_as_nparray + + + XtractDenoiser.plot + + + XtractDenoiser.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + XtractDenoiser.input_parameters + XtractDenoiser.input_signal + XtractDenoiser.output_denoised_signals + XtractDenoiser.output_noise_signals + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..f8643dc54 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_automatic_estimation.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_automatic_estimation.rst.txt new file mode 100644 index 000000000..72fee81c2 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_automatic_estimation.rst.txt @@ -0,0 +1,6 @@ +create\_noise\_psd\_from\_automatic\_estimation +=============================================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.create_noise_psd_from_automatic_estimation \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_noise_samples.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_noise_samples.rst.txt new file mode 100644 index 000000000..749e4691f --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_noise_samples.rst.txt @@ -0,0 +1,6 @@ +create\_noise\_psd\_from\_noise\_samples +======================================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.create_noise_psd_from_noise_samples \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_white_noise_level.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_white_noise_level.rst.txt new file mode 100644 index 000000000..cef39281d --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.create_noise_psd_from_white_noise_level.rst.txt @@ -0,0 +1,6 @@ +create\_noise\_psd\_from\_white\_noise\_level +============================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.create_noise_psd_from_white_noise_level \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output.rst.txt new file mode 100644 index 000000000..53291cea8 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..1ed187248 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_parameters_as_generic_data_container.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_parameters_as_generic_data_container.rst.txt new file mode 100644 index 000000000..f6dada830 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.get_parameters_as_generic_data_container.rst.txt @@ -0,0 +1,6 @@ +get\_parameters\_as\_generic\_data\_container +============================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.get_parameters_as_generic_data_container \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.noise_psd.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.noise_psd.rst.txt new file mode 100644 index 000000000..776625733 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.noise_psd.rst.txt @@ -0,0 +1,6 @@ +noise\_psd +========== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractDenoiserParameters.noise_psd \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.plot.rst.txt new file mode 100644 index 000000000..07b21407b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.process.rst.txt new file mode 100644 index 000000000..b451d0310 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractDenoiserParameters.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.rst.txt new file mode 100644 index 000000000..22e595d88 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractDenoiserParameters.rst.txt @@ -0,0 +1,56 @@ +XtractDenoiserParameters +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoclass:: XtractDenoiserParameters + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + XtractDenoiserParameters.convert_fields_container_to_np_array + + + XtractDenoiserParameters.create_noise_psd_from_automatic_estimation + + + XtractDenoiserParameters.create_noise_psd_from_noise_samples + + + XtractDenoiserParameters.create_noise_psd_from_white_noise_level + + + XtractDenoiserParameters.get_output + + + XtractDenoiserParameters.get_output_as_nparray + + + XtractDenoiserParameters.get_parameters_as_generic_data_container + + + XtractDenoiserParameters.plot + + + XtractDenoiserParameters.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + XtractDenoiserParameters.noise_psd + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..64643c950 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonal.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output.rst.txt new file mode 100644 index 000000000..234d85b13 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonal.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..8cfb50a40 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonal.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_parameters.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_parameters.rst.txt new file mode 100644 index 000000000..ea22d652a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_parameters.rst.txt @@ -0,0 +1,6 @@ +input\_parameters +================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonal.input_parameters \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_signal.rst.txt new file mode 100644 index 000000000..aebc4a60c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.input_signal.rst.txt @@ -0,0 +1,6 @@ +input\_signal +============= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonal.input_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_non_tonal_signals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_non_tonal_signals.rst.txt new file mode 100644 index 000000000..fa20a1009 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_non_tonal_signals.rst.txt @@ -0,0 +1,6 @@ +output\_non\_tonal\_signals +=========================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonal.output_non_tonal_signals \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_tonal_signals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_tonal_signals.rst.txt new file mode 100644 index 000000000..b1af9573a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.output_tonal_signals.rst.txt @@ -0,0 +1,6 @@ +output\_tonal\_signals +====================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonal.output_tonal_signals \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.plot.rst.txt new file mode 100644 index 000000000..2618af252 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonal.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.process.rst.txt new file mode 100644 index 000000000..f1657f0b6 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonal.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.rst.txt new file mode 100644 index 000000000..280238e72 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonal.rst.txt @@ -0,0 +1,47 @@ +XtractTonal +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoclass:: XtractTonal + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + XtractTonal.convert_fields_container_to_np_array + + + XtractTonal.get_output + + + XtractTonal.get_output_as_nparray + + + XtractTonal.plot + + + XtractTonal.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + XtractTonal.input_parameters + XtractTonal.input_signal + XtractTonal.output_non_tonal_signals + XtractTonal.output_tonal_signals + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..b015d0938 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonalParameters.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.fft_size.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.fft_size.rst.txt new file mode 100644 index 000000000..15c2e63ed --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.fft_size.rst.txt @@ -0,0 +1,6 @@ +fft\_size +========= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonalParameters.fft_size \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output.rst.txt new file mode 100644 index 000000000..f80ec5b8a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonalParameters.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..ae25b6501 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonalParameters.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_parameters_as_generic_data_container.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_parameters_as_generic_data_container.rst.txt new file mode 100644 index 000000000..51ee826da --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.get_parameters_as_generic_data_container.rst.txt @@ -0,0 +1,6 @@ +get\_parameters\_as\_generic\_data\_container +============================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonalParameters.get_parameters_as_generic_data_container \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.intertonal_gap.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.intertonal_gap.rst.txt new file mode 100644 index 000000000..1d0dfc2d8 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.intertonal_gap.rst.txt @@ -0,0 +1,6 @@ +intertonal\_gap +=============== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonalParameters.intertonal_gap \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.local_emergence.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.local_emergence.rst.txt new file mode 100644 index 000000000..296e8f207 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.local_emergence.rst.txt @@ -0,0 +1,6 @@ +local\_emergence +================ + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonalParameters.local_emergence \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.maximum_slope.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.maximum_slope.rst.txt new file mode 100644 index 000000000..1250243db --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.maximum_slope.rst.txt @@ -0,0 +1,6 @@ +maximum\_slope +============== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonalParameters.maximum_slope \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.minimum_duration.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.minimum_duration.rst.txt new file mode 100644 index 000000000..255dfc85c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.minimum_duration.rst.txt @@ -0,0 +1,6 @@ +minimum\_duration +================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonalParameters.minimum_duration \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.plot.rst.txt new file mode 100644 index 000000000..fcc2f8255 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonalParameters.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.process.rst.txt new file mode 100644 index 000000000..8adf8d907 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTonalParameters.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.regularity.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.regularity.rst.txt new file mode 100644 index 000000000..20c96b632 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.regularity.rst.txt @@ -0,0 +1,6 @@ +regularity +========== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTonalParameters.regularity \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.rst.txt new file mode 100644 index 000000000..96bc99005 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTonalParameters.rst.txt @@ -0,0 +1,52 @@ +XtractTonalParameters +===================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoclass:: XtractTonalParameters + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + XtractTonalParameters.convert_fields_container_to_np_array + + + XtractTonalParameters.get_output + + + XtractTonalParameters.get_output_as_nparray + + + XtractTonalParameters.get_parameters_as_generic_data_container + + + XtractTonalParameters.plot + + + XtractTonalParameters.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + XtractTonalParameters.fft_size + XtractTonalParameters.intertonal_gap + XtractTonalParameters.local_emergence + XtractTonalParameters.maximum_slope + XtractTonalParameters.minimum_duration + XtractTonalParameters.regularity + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..c93ff4c12 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransient.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output.rst.txt new file mode 100644 index 000000000..34b81a941 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransient.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..14592dd82 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransient.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_parameters.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_parameters.rst.txt new file mode 100644 index 000000000..73ddefa29 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_parameters.rst.txt @@ -0,0 +1,6 @@ +input\_parameters +================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTransient.input_parameters \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_signal.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_signal.rst.txt new file mode 100644 index 000000000..973a67402 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.input_signal.rst.txt @@ -0,0 +1,6 @@ +input\_signal +============= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTransient.input_signal \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_non_transient_signals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_non_transient_signals.rst.txt new file mode 100644 index 000000000..33603cfba --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_non_transient_signals.rst.txt @@ -0,0 +1,6 @@ +output\_non\_transient\_signals +=============================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTransient.output_non_transient_signals \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_transient_signals.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_transient_signals.rst.txt new file mode 100644 index 000000000..42c8ab785 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.output_transient_signals.rst.txt @@ -0,0 +1,6 @@ +output\_transient\_signals +========================== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTransient.output_transient_signals \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.plot.rst.txt new file mode 100644 index 000000000..659f1277b --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransient.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.process.rst.txt new file mode 100644 index 000000000..bcc79af94 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransient.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.rst.txt new file mode 100644 index 000000000..fb7b46fdd --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransient.rst.txt @@ -0,0 +1,47 @@ +XtractTransient +=============== + +.. currentmodule:: ansys.sound.core.xtract + +.. autoclass:: XtractTransient + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + XtractTransient.convert_fields_container_to_np_array + + + XtractTransient.get_output + + + XtractTransient.get_output_as_nparray + + + XtractTransient.plot + + + XtractTransient.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + XtractTransient.input_parameters + XtractTransient.input_signal + XtractTransient.output_non_transient_signals + XtractTransient.output_transient_signals + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.convert_fields_container_to_np_array.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.convert_fields_container_to_np_array.rst.txt new file mode 100644 index 000000000..41d88b6b9 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.convert_fields_container_to_np_array.rst.txt @@ -0,0 +1,6 @@ +convert\_fields\_container\_to\_np\_array +========================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransientParameters.convert_fields_container_to_np_array \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output.rst.txt new file mode 100644 index 000000000..8ffdea950 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output.rst.txt @@ -0,0 +1,6 @@ +get\_output +=========== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransientParameters.get_output \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output_as_nparray.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output_as_nparray.rst.txt new file mode 100644 index 000000000..c3d07e421 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_output_as_nparray.rst.txt @@ -0,0 +1,6 @@ +get\_output\_as\_nparray +======================== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransientParameters.get_output_as_nparray \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_parameters_as_generic_data_container.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_parameters_as_generic_data_container.rst.txt new file mode 100644 index 000000000..3d0aa190c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.get_parameters_as_generic_data_container.rst.txt @@ -0,0 +1,6 @@ +get\_parameters\_as\_generic\_data\_container +============================================= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransientParameters.get_parameters_as_generic_data_container \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.lower_threshold.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.lower_threshold.rst.txt new file mode 100644 index 000000000..318184e64 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.lower_threshold.rst.txt @@ -0,0 +1,6 @@ +lower\_threshold +================ + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTransientParameters.lower_threshold \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.plot.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.plot.rst.txt new file mode 100644 index 000000000..5f787fc4a --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.plot.rst.txt @@ -0,0 +1,6 @@ +plot +==== + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransientParameters.plot \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.process.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.process.rst.txt new file mode 100644 index 000000000..735ca68ff --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.process.rst.txt @@ -0,0 +1,6 @@ +process +======= + +.. currentmodule:: ansys.sound.core.xtract + +.. automethod:: XtractTransientParameters.process \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.rst.txt new file mode 100644 index 000000000..117a7577c --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.rst.txt @@ -0,0 +1,48 @@ +XtractTransientParameters +========================= + +.. currentmodule:: ansys.sound.core.xtract + +.. autoclass:: XtractTransientParameters + + + + + .. rubric:: Methods + + .. autosummary:: + :toctree: + + + + XtractTransientParameters.convert_fields_container_to_np_array + + + XtractTransientParameters.get_output + + + XtractTransientParameters.get_output_as_nparray + + + XtractTransientParameters.get_parameters_as_generic_data_container + + + XtractTransientParameters.plot + + + XtractTransientParameters.process + + + + + + + .. rubric:: Attributes + + .. autosummary:: + :toctree: + + XtractTransientParameters.lower_threshold + XtractTransientParameters.upper_threshold + + \ No newline at end of file diff --git a/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.upper_threshold.rst.txt b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.upper_threshold.rst.txt new file mode 100644 index 000000000..3e9653c74 --- /dev/null +++ b/version/0.1/_sources/api/_autosummary/ansys.sound.core.xtract.XtractTransientParameters.upper_threshold.rst.txt @@ -0,0 +1,6 @@ +upper\_threshold +================ + +.. currentmodule:: ansys.sound.core.xtract + +.. autoproperty:: XtractTransientParameters.upper_threshold \ No newline at end of file diff --git a/version/0.1/_sources/api/helpers.rst.txt b/version/0.1/_sources/api/helpers.rst.txt new file mode 100644 index 000000000..8a4d64b00 --- /dev/null +++ b/version/0.1/_sources/api/helpers.rst.txt @@ -0,0 +1,10 @@ +Server helpers +-------------- + +.. module:: ansys.sound.core.server_helpers + +.. autosummary:: + :toctree: _autosummary + + _connect_to_or_start_server.connect_to_or_start_server + _validate_dpf_sound_connection.validate_dpf_sound_connection diff --git a/version/0.1/_sources/api/index.rst.txt b/version/0.1/_sources/api/index.rst.txt new file mode 100644 index 000000000..9db0bbd18 --- /dev/null +++ b/version/0.1/_sources/api/index.rst.txt @@ -0,0 +1,15 @@ +API reference +============= + +This section describes how to use Python to interact programmatically with PyAnsys Sound. + +.. module:: ansys.sound.core + +.. toctree:: + :maxdepth: 2 + + signal_utilities + spectrogram_processing + psychoacoustics + xtract + helpers \ No newline at end of file diff --git a/version/0.1/_sources/api/psychoacoustics.rst.txt b/version/0.1/_sources/api/psychoacoustics.rst.txt new file mode 100644 index 000000000..1c426260d --- /dev/null +++ b/version/0.1/_sources/api/psychoacoustics.rst.txt @@ -0,0 +1,15 @@ +Psychoacoustics +--------------- + +.. module:: ansys.sound.core.psychoacoustics + +.. autosummary:: + :toctree: _autosummary + + LoudnessISO532_1_Stationary + LoudnessISO532_1_TimeVarying + Sharpness + Roughness + FluctuationStrength + ProminenceRatio + ToneToNoiseRatio diff --git a/version/0.1/_sources/api/signal_utilities.rst.txt b/version/0.1/_sources/api/signal_utilities.rst.txt new file mode 100644 index 000000000..505bdaacc --- /dev/null +++ b/version/0.1/_sources/api/signal_utilities.rst.txt @@ -0,0 +1,16 @@ +Signal utilities +---------------- + +.. module:: ansys.sound.core.signal_utilities + +.. autosummary:: + :toctree: _autosummary + + ApplyGain + CreateSoundField + CropSignal + LoadWav + Resample + SumSignals + WriteWav + ZeroPad \ No newline at end of file diff --git a/version/0.1/_sources/api/spectrogram_processing.rst.txt b/version/0.1/_sources/api/spectrogram_processing.rst.txt new file mode 100644 index 000000000..6867635a1 --- /dev/null +++ b/version/0.1/_sources/api/spectrogram_processing.rst.txt @@ -0,0 +1,11 @@ +Spectrogram processing +---------------------- + +.. module:: ansys.sound.core.spectrogram_processing + +.. autosummary:: + :toctree: _autosummary + + Stft + Istft + IsolateOrders \ No newline at end of file diff --git a/version/0.1/_sources/api/xtract.rst.txt b/version/0.1/_sources/api/xtract.rst.txt new file mode 100644 index 000000000..df3c0b213 --- /dev/null +++ b/version/0.1/_sources/api/xtract.rst.txt @@ -0,0 +1,15 @@ +Xtract +------ + +.. module:: ansys.sound.core.xtract + +.. autosummary:: + :toctree: _autosummary + + Xtract + XtractDenoiser + XtractDenoiserParameters + XtractTonal + XtractTonalParameters + XtractTransient + XtractTransientParameters diff --git a/version/0.1/_sources/contribute.rst.txt b/version/0.1/_sources/contribute.rst.txt new file mode 100644 index 000000000..5aaea5dfb --- /dev/null +++ b/version/0.1/_sources/contribute.rst.txt @@ -0,0 +1,134 @@ +.. _ref_contribute: + +Contribute +---------- +Overall guidance on contributing to a PyAnsys library appears in the +`Contributing `_ topic +in the *PyAnsys developer's guide*. Ensure that you are thoroughly familiar +with this guide before attempting to contribute to PyAnsys Sound. + +The following contribution information is specific to PyAnsys Sound. + + +Install in development mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Installing PyAnsys Sound in development mode allows you to modify and enhance +the source. + +.. note:: + For information on prerequisites, see :ref:`prerequisistes`. + +Perform these steps to install PyAnsys Sound in developer mode: + +#. Clone the repository: + + .. code:: bash + + git clone https://github.com/ansys/pyansys-sound + cd pyansys-sound + +#. Create a fresh-clean Python `virtual environment `_ + and activate it. + + .. code:: bash + + # Create a virtual environment + python -m venv .venv + + # Activate it in a POSIX system + source .venv/bin/activate + + # Activate it in Windows CMD environment + .venv\Scripts\activate.bat + + # Activate it in Windows Powershell + .venv\Scripts\Activate.ps1 + +#. Install dependencies: + + .. code:: bash + + pip install -e . + +#. Install additional requirements for tests and documentation (if needed): + + .. code:: bash + + pip install -e.[doc,tests] + +Test +---- + +There are different ways to run the PyAnsys Sound tests, depending on how the DPF +server is started. + +- Run tests with a Docker container. + + For information on getting and running the DPF Server Docker image, see :ref:`DPF Server Docker image`. + Run the tests with this command: + + .. code:: bash + + pytest . + +- Run tests with a Docker container from GitHub with these commands: + + .. code:: bash + + docker pull ghcr.io/ansys/ansys-sound:latest + pytest . + + +Run style checks +---------------- + +The style checks use `pre-commit`_, which can be run from a Powershell terminal: + +.. code:: bash + + pre-commit run --all-files + + +You can also install this as a Git pre-commit hook by running this command: + +.. code:: bash + + pre-commit install + +Build documentation +------------------- + +Before you can build the documentation, you must get and run the DPF Server Docker image. +For more information, see :ref:`DPF Server Docker image`. + +On Windows, build the documentation with this command: + +.. code:: powershell + + .\doc\make.bat html + +You can use the latest container from GitHub to build it with the following command: + +.. code:: powershell + + docker pull ghcr.io/ansys/ansys-dpf-sound:latest + docker run -d -p 6780:50052 -e ANSYSLMD_LICENSE_FILE=1055@mylicserver -e ANSYS_DPF_ACCEPT_LA=Y ghcr.io/ansys/ansys-sound:latest + docker run -d -e "ANSYS_DPF_ACCEPT_LA=Y" -e "ANSYSLMD_LICENSE_FILE=1055@mylicserver" -v $env:LOCALAPPDATA\Ansys\ansys_sound_core\examples:C:\data -p 6780:50052 ghcr.io/ansys/ansys-dpf-sound:latest + .\doc\make.bat html + + +.. LINKS AND REFERENCES +.. _black: https://github.com/psf/black +.. _flake8: https://flake8.pycqa.org/en/latest/ +.. _isort: https://github.com/PyCQA/isort +.. _PyAnsys Developer's guide: https://dev.docs.pyansys.com/ +.. _pre-commit: https://pre-commit.com/ +.. _pytest: https://docs.pytest.org/en/stable/ +.. _Sphinx: https://www.sphinx-doc.org/en/master/ +.. _pip: https://pypi.org/project/pip/ +.. _tox: https://tox.wiki/en/stable/ +.. _venv: https://docs.python.org/3/library/venv.html +.. _Getting the DPF Server Docker image: https://sound.docs.pyansys.com/version/stable/getting_started.html#getting-the-dpf-server-docker-image +.. _Examples: https://sound.docs.pyansys.com/version/stable/examples/index.html +.. _Ansys DPF: https://dpf.docs.pyansys.com/version/stable/ \ No newline at end of file diff --git a/version/0.1/_sources/examples/gallery_examples/001_initialize_server_and_deal_with_license.rst.txt b/version/0.1/_sources/examples/gallery_examples/001_initialize_server_and_deal_with_license.rst.txt new file mode 100644 index 000000000..05ea4dc0c --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/001_initialize_server_and_deal_with_license.rst.txt @@ -0,0 +1,288 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples\gallery_examples\001_initialize_server_and_deal_with_license.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_gallery_examples_001_initialize_server_and_deal_with_license.py: + + +.. _initialize_server_and_deal_with_license: + +Initialize PyAnsys Sound and check out the license +-------------------------------------------------- + +This example shows how to initialize PyDPF-Core, load DPF Sound, and check out the +required Ansys license (``increment avrxp_snd_level1``) only once. It also shows +how to connect to the DPF server, verify where it is located, and get other useful +information. + +This example also demonstrates the use of the LicenseContextManager, a mechanism that lets you +check out the license only once for the duration of the session, which greatly improves performance. +It shows the execution time of a simple DPF Sound operator when you do not use the +LicenseContextManager versus when you do use it. + +**Prerequisites** + +Ensure that you have installed PyDPF-Core and DPF Sound according to procedures in +the PyDPF-Core documentation: + +- If you have installed the latest Ansys release, see `Install using pip + `_. +- If you want to use the DPF standalone version, see `Install DPF Server + `_. + +.. GENERATED FROM PYTHON SOURCE LINES 53-56 + +Perform required imports +~~~~~~~~~~~~~~~~~~~~~~~~ +Perform the required imports: + +.. GENERATED FROM PYTHON SOURCE LINES 56-67 + +.. code-block:: Python + + + # Load Ansys and other libraries. + import datetime + + import ansys.dpf.core as dpf + + from ansys.sound.core.examples_helpers import download_flute_wav + from ansys.sound.core.server_helpers import connect_to_or_start_server + from ansys.sound.core.signal_utilities import LoadWav + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 71-78 + +Use a DPF server without a LicenseContextManager +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Initialize a DPF server without using a LicenseContextManager. + +**Note**: When ``use_license_context=False``, the license is checked out each time +you use a DPF Sound operator. + +.. GENERATED FROM PYTHON SOURCE LINES 78-92 + +.. code-block:: Python + + + + # Connect to a remote server or start a local server without using a LicenseContextManager + print("Connecting to the server without using a LicenseContextManager") + my_server = connect_to_or_start_server(use_license_context=False) + + # Check if you are using a local or remote server + has_local_server = dpf.server.has_local_server() + print(f"Local server: {has_local_server}") + + # If using a local server, display the path to the server + if has_local_server == True: + print(f"Local server path (server variable): {my_server.ansys_path}") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Connecting to the server without using a LicenseContextManager + Local server: True + Local server path (server variable): None + + + + +.. GENERATED FROM PYTHON SOURCE LINES 93-94 + +Display information about the server that you are using. + +.. GENERATED FROM PYTHON SOURCE LINES 94-96 + +.. code-block:: Python + + print(f"Server information: {my_server.info}") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Server information: {'server_ip': '172.27.38.10', 'server_port': 50052, 'server_process_id': 1652, 'server_version': '9.1', 'os': 'nt', 'path': None} + + + + +.. GENERATED FROM PYTHON SOURCE LINES 97-99 + +Execute the PyAnsys Sound ``LoadWav`` operator several times in a row +and measure the execution time. + +.. GENERATED FROM PYTHON SOURCE LINES 99-115 + +.. code-block:: Python + + + path_flute_wav = download_flute_wav() + + for i in range(5): + now = datetime.datetime.now() + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + fc_signal = wav_loader.get_output() + later = datetime.datetime.now() + execution_time = later - now + print( + f"Elapsed time (loop {i+1}): " + f"{execution_time.seconds + execution_time.microseconds/1e6}" + f" seconds" + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Elapsed time (loop 1): 10.72009 seconds + Elapsed time (loop 2): 3.848715 seconds + Elapsed time (loop 3): 3.864041 seconds + Elapsed time (loop 4): 3.881709 seconds + Elapsed time (loop 5): 3.847 seconds + + + + +.. GENERATED FROM PYTHON SOURCE LINES 116-117 + +Disconnect (shut down) the server and release the license increment. + +.. GENERATED FROM PYTHON SOURCE LINES 117-120 + +.. code-block:: Python + + print("Disconnecting from the server and releasing the license increment.") + my_server = None + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Disconnecting from the server and releasing the license increment. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 121-128 + +Use a DPF server with a LicenseContextManager +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Initialize a DPF server using a LicenseContextManager and execute the same code as run previously. + +**Note**: The LicenseContextManager is a mechanism that checks out a license increment +when entering the context and releases it when exiting the context. + +.. GENERATED FROM PYTHON SOURCE LINES 128-147 + +.. code-block:: Python + + + # Connect to a remote server or start a local server using a LicenseContextManager + print("Connecting to the server using a LicenseContextManager") + my_server = connect_to_or_start_server(use_license_context=True) + + # Execute the same and measure the execution time + for i in range(5): + now = datetime.datetime.now() + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + fc_signal = wav_loader.get_output() + later = datetime.datetime.now() + execution_time = later - now + print( + f"Elapsed time (loop {i+1}): " + f"{execution_time.seconds + execution_time.microseconds / 1e6}" + f" seconds" + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Connecting to the server using a LicenseContextManager + Elapsed time (loop 1): 0.127616 seconds + Elapsed time (loop 2): 0.122754 seconds + Elapsed time (loop 3): 0.115687 seconds + Elapsed time (loop 4): 0.134625 seconds + Elapsed time (loop 5): 0.133479 seconds + + + + +.. GENERATED FROM PYTHON SOURCE LINES 148-151 + +You can see that the execution time is much faster when you use a LicenseContextManager. +This is because when a LicenseContactManager is not used, the license is checked out +each time you use a DPF Sound operator. + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 29.813 seconds) + + +.. _sphx_glr_download_examples_gallery_examples_001_initialize_server_and_deal_with_license.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: 001_initialize_server_and_deal_with_license.ipynb <001_initialize_server_and_deal_with_license.ipynb>` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: 001_initialize_server_and_deal_with_license.py <001_initialize_server_and_deal_with_license.py>` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/version/0.1/_sources/examples/gallery_examples/002_load_resample_amplify_write_wav_files.rst.txt b/version/0.1/_sources/examples/gallery_examples/002_load_resample_amplify_write_wav_files.rst.txt new file mode 100644 index 000000000..b85ace135 --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/002_load_resample_amplify_write_wav_files.rst.txt @@ -0,0 +1,244 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples\gallery_examples\002_load_resample_amplify_write_wav_files.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_gallery_examples_002_load_resample_amplify_write_wav_files.py: + + +.. _load_resample_amplify_write_wav_files_example: + +Load a signal from a WAV file +----------------------------- + +This example shows how to load a signal from a WAV file, modify the signal's +sampling frequency, and amplify it. It also shows how to access the corresponding +data, display it using Matplotlib, and then write the modified signal to the disk as +a new WAV file. + +.. GENERATED FROM PYTHON SOURCE LINES 36-40 + +Set up analysis +~~~~~~~~~~~~~~~ +Setting up the analysis consists of loading Ansys libraries, connecting to the +DPF server, and retrieving the example files. + +.. GENERATED FROM PYTHON SOURCE LINES 40-51 + +.. code-block:: Python + + + # Load Ansys and other libraries. + import matplotlib.pyplot as plt + + from ansys.sound.core.examples_helpers import download_flute_wav + from ansys.sound.core.server_helpers import connect_to_or_start_server + from ansys.sound.core.signal_utilities import ApplyGain, LoadWav, Resample, WriteWav + + # Connect to a remote server or start a local server + server = connect_to_or_start_server() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 52-58 + +Load a signal +~~~~~~~~~~~~~ +Load a signal from a WAV file using the ``LoadWav`` class. It is returned as a DPF +fields container. For more information, see `fields_container +`_ +in the DPF-Core API documentation. + +.. GENERATED FROM PYTHON SOURCE LINES 58-71 + +.. code-block:: Python + + + # Return the input data of the example file + path_flute_wav = download_flute_wav() + + # Load the WAV file. + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + fc_signal_original = wav_loader.get_output() + + t1 = fc_signal_original[0].time_freq_support.time_frequencies.data + sf1 = 1.0 / (t1[1] - t1[0]) + print(f"The sampling frequency of the original signal is {int(sf1)} Hz.") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + The sampling frequency of the original signal is 44100 Hz. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 72-75 + +Resample the signal +~~~~~~~~~~~~~~~~~~~ +Change the sampling frequency of the loaded signal. + +.. GENERATED FROM PYTHON SOURCE LINES 75-83 + +.. code-block:: Python + + resampler = Resample(fc_signal_original, new_sampling_frequency=20000.0) + resampler.process() + fc_signal_resampled = resampler.get_output() + + t2 = fc_signal_resampled[0].time_freq_support.time_frequencies.data + sf2 = 1.0 / (t2[1] - t2[0]) + print(f"The new sampling frequency of the signal is {int(sf2)} Hz.") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + The new sampling frequency of the signal is 20000 Hz. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 84-87 + +Apply a gain to the signal +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Amplify the resampled signal by 10 decibels. + +.. GENERATED FROM PYTHON SOURCE LINES 87-92 + +.. code-block:: Python + + gain = 10.0 + gain_applier = ApplyGain(fc_signal_resampled, gain=gain, gain_in_db=True) + gain_applier.process() + fc_signal_modified = gain_applier.get_output() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 93-96 + +Plot signals +~~~~~~~~~~~~ +Plot both the original signal and modified signal. + +.. GENERATED FROM PYTHON SOURCE LINES 96-121 + +.. code-block:: Python + + + # Get the signals as nparray + data_original = wav_loader.get_output_as_nparray() + data_modified = gain_applier.get_output_as_nparray() + + # Prepare the figure + fig, axs = plt.subplots(2) + fig.suptitle("Signals") + + axs[0].plot(t1, data_original, color="g", label=f"original signal, sf={int(sf1)} Hz") + axs[0].set_ylabel("Pa") + axs[0].legend(loc="upper right") + axs[0].set_ylim([-3, 3]) + + axs[1].plot( + t2, data_modified, color="r", label=f"modified signal, sf={int(sf2)} Hz, gain={gain} dBSPL" + ) + axs[1].set_xlabel("Time(s)") + axs[1].set_ylabel("Amplitude(Pa)") + axs[1].legend(loc="upper right") + axs[1].set_ylim([-3, 3]) + + # Display the figure + plt.show() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_002_load_resample_amplify_write_wav_files_001.png + :alt: Signals + :srcset: /examples/gallery_examples/images/sphx_glr_002_load_resample_amplify_write_wav_files_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 122-125 + +Write the signal as a WAV file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Write the modified signal to the disk as a WAV file. + +.. GENERATED FROM PYTHON SOURCE LINES 125-128 + +.. code-block:: Python + + output_path = path_flute_wav[:-4] + "_modified.wav" # "[-4]" is to remove the ".wav" extension + wav_writer = WriteWav(path_to_write=output_path, signal=fc_signal_modified, bit_depth="int16") + wav_writer.process() + + + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.810 seconds) + + +.. _sphx_glr_download_examples_gallery_examples_002_load_resample_amplify_write_wav_files.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: 002_load_resample_amplify_write_wav_files.ipynb <002_load_resample_amplify_write_wav_files.ipynb>` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: 002_load_resample_amplify_write_wav_files.py <002_load_resample_amplify_write_wav_files.py>` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/version/0.1/_sources/examples/gallery_examples/003_compute_stft.rst.txt b/version/0.1/_sources/examples/gallery_examples/003_compute_stft.rst.txt new file mode 100644 index 000000000..8ba84456d --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/003_compute_stft.rst.txt @@ -0,0 +1,235 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples\gallery_examples\003_compute_stft.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_gallery_examples_003_compute_stft.py: + + +.. _compute_stft_example: + +Compute the STFT and ISTFT +-------------------------- + +This example shows how to compute the short-time Fourier transform (STFT) of a signal. +It also shows how to compute the inverse short-time Fourier transform (ISTFT) from a +STFT matrix and get a signal. + +.. GENERATED FROM PYTHON SOURCE LINES 35-39 + +Set up analysis +~~~~~~~~~~~~~~~ +Setting up the analysis consists of loading Ansys libraries, connecting to the +DPF server, and retrieving the example files. + +.. GENERATED FROM PYTHON SOURCE LINES 39-49 + +.. code-block:: Python + + + # Load Ansys libraries. + from ansys.sound.core.examples_helpers import download_flute_wav + from ansys.sound.core.server_helpers import connect_to_or_start_server + from ansys.sound.core.signal_utilities import LoadWav + from ansys.sound.core.spectrogram_processing import Istft, Stft + + # Connect to a remote server or start a local server + my_server = connect_to_or_start_server(use_license_context=True) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 50-56 + +Load a signal +~~~~~~~~~~~~~ +Load a signal from a WAV file using the ``LoadWav`` class. It is returned as a DPF +field container. For more information, see `fields_container +`_ +in the DPF-Core API documentation. + +.. GENERATED FROM PYTHON SOURCE LINES 56-68 + +.. code-block:: Python + + + # Return the input data of the example file + path_flute_wav = download_flute_wav() + + # Load the WAV file + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + fc_signal = wav_loader.get_output() + + # Plot the input signal + wav_loader.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_003_compute_stft_001.png + :alt: flute + :srcset: /examples/gallery_examples/images/sphx_glr_003_compute_stft_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 69-71 + +Compute and plot STFT +~~~~~~~~~~~~~~~~~~~~~ + +.. GENERATED FROM PYTHON SOURCE LINES 71-83 + +.. code-block:: Python + + + # Instantiate an instance of the ``Stft`` class using the previously loaded signal + # as an input. Use an FFT size of 1024 points and then display the STFT colormap. + + stft = Stft(fc_signal, fft_size=1024) + + # Process the STFT + stft.process() + + # Plot the output + stft.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_003_compute_stft_002.png + :alt: STFT, Amplitude, Phase + :srcset: /examples/gallery_examples/images/sphx_glr_003_compute_stft_002.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 84-86 + +Modify the STFT parameters using the setters of the ``Stft`` class. +Display the new STFT colormap. + +.. GENERATED FROM PYTHON SOURCE LINES 86-97 + +.. code-block:: Python + + + stft.fft_size = 4096 + stft.window_overlap = 0.95 + stft.window_type = "BARTLETT" + + # Reprocess the STFT with the new parameters + stft.process() + + # Plot the modified output + stft.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_003_compute_stft_003.png + :alt: STFT, Amplitude, Phase + :srcset: /examples/gallery_examples/images/sphx_glr_003_compute_stft_003.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 98-100 + +Compute and plot ISTFT +~~~~~~~~~~~~~~~~~~~~~ + +.. GENERATED FROM PYTHON SOURCE LINES 100-112 + +.. code-block:: Python + + + # Obtain a time-domain signal using the ``Istft`` class. + # The input of the ``Istft`` class is the output STFT object previously computed. + + fc_stft = stft.get_output() + + # Instantiate the class + istft = Istft(fc_stft) + + # Process the ISTFT + istft.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 113-114 + +Plot the output, which is the original signal. + +.. GENERATED FROM PYTHON SOURCE LINES 114-116 + +.. code-block:: Python + + + istft.plot() + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_003_compute_stft_004.png + :alt: 003 compute stft + :srcset: /examples/gallery_examples/images/sphx_glr_003_compute_stft_004.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (1 minutes 20.899 seconds) + + +.. _sphx_glr_download_examples_gallery_examples_003_compute_stft.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: 003_compute_stft.ipynb <003_compute_stft.ipynb>` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: 003_compute_stft.py <003_compute_stft.py>` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/version/0.1/_sources/examples/gallery_examples/004_isolate_orders.rst.txt b/version/0.1/_sources/examples/gallery_examples/004_isolate_orders.rst.txt new file mode 100644 index 000000000..d1726c832 --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/004_isolate_orders.rst.txt @@ -0,0 +1,442 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples\gallery_examples\004_isolate_orders.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_gallery_examples_004_isolate_orders.py: + + +.. _isolate_orders_example: + +Isolate orders +-------------- + +Orders are harmonic and partial components in the sound related to the speed of a +rotating machine. This example shows how to isolate orders in a signal containing an RPM profile. +It also uses additional classes from PyAnsys Sound to compute spectrograms +and the loudness of the isolated signals. + +.. GENERATED FROM PYTHON SOURCE LINES 35-39 + +.. code-block:: Python + + + # Maximum frequency for STFT plots, change according to your need + MAX_FREQUENCY_PLOT_STFT = 2000.0 + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 40-45 + +Set up analysis +~~~~~~~~~~~~~~~ +Setting up the analysis consists of loading Ansys libraries, connecting to the +DPF server, and retrieving the example files. + + +.. GENERATED FROM PYTHON SOURCE LINES 45-67 + +.. code-block:: Python + + + # Load Ansys libraries. + import os + import pathlib + + import matplotlib.pyplot as plt + import numpy as np + + from ansys.sound.core.examples_helpers import ( + download_accel_with_rpm_2_wav, + download_accel_with_rpm_3_wav, + download_accel_with_rpm_wav, + ) + from ansys.sound.core.psychoacoustics import LoudnessISO532_1_Stationary + from ansys.sound.core.server_helpers import connect_to_or_start_server + from ansys.sound.core.signal_utilities import LoadWav, WriteWav + from ansys.sound.core.spectrogram_processing import IsolateOrders, Stft + + # Connect to a remote or start a local server + my_server = connect_to_or_start_server() + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 68-74 + +Define a custom function for STFT plots +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Defining a custom function for STFT plots lets you have +more control over what you are displaying. +While you could use the ``Stft.plot()`` method, the custom function +defined here restricts the frequency range of the plot. + +.. GENERATED FROM PYTHON SOURCE LINES 74-117 + +.. code-block:: Python + + def plot_stft(stft_class, vmax): + out = stft_class.get_output_as_nparray() + + # Extract first half of the STFT (second half is symmetrical) + half_nfft = int(out.shape[0] / 2) + 1 + magnitude = stft_class.get_stft_magnitude_as_nparray() + + # Voluntarily ignore a numpy warning + np.seterr(divide="ignore") + magnitude = 20 * np.log10(magnitude[0:half_nfft, :]) + np.seterr(divide="warn") + + # Obtain sampling frequency, time steps, and number of time samples + fs = 1.0 / ( + stft_class.signal.time_freq_support.time_frequencies.data[1] + - stft_class.signal.time_freq_support.time_frequencies.data[0] + ) + time_step = np.floor(stft_class.fft_size * (1.0 - stft_class.window_overlap) + 0.5) / fs + num_time_index = len(stft_class.get_output().get_available_ids_for_label("time")) + + # Define boundaries of the plot + extent = [0, time_step * num_time_index, 0.0, fs / 2.0] + + # Plot + plt.imshow( + magnitude, + origin="lower", + aspect="auto", + cmap="jet", + extent=extent, + vmin=vmax - 70.0, + vmax=vmax, + ) + plt.colorbar(label="Amplitude (dB SPL)") + plt.ylabel("Frequency (Hz)") + plt.xlabel("Time (s)") + plt.ylim( + [0.0, MAX_FREQUENCY_PLOT_STFT] + ) # Change the value of MAX_FREQUENCY_PLOT_STFT if needed + plt.title("STFT") + plt.show() + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 118-126 + +Load a signal with an RPM profile +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Load a signal that has been generated with Ansys Sound Analysis +and Specification (SAS) from a WAV file using the ``LoadWav`` class. +This class contains two channels: + +- The actual signal (an acceleration recording) +- The associated RPM profile + +.. GENERATED FROM PYTHON SOURCE LINES 126-154 + +.. code-block:: Python + + + # Return the input data of the example file + path_accel_wav = download_accel_with_rpm_wav() + + # Load the WAV file. + wav_loader = LoadWav(path_accel_wav) + wav_loader.process() + fc_signal = wav_loader.get_output() + + # Extract the audio signal and the RPM profile + wav_signal, rpm_signal = wav_loader.get_output_as_nparray() + + # Extract time support associated with the signal + time_support = fc_signal[0].time_freq_support.time_frequencies.data + + # Plot the signal and its associated RPM profile + fig, ax = plt.subplots(nrows=2, sharex=True) + ax[0].plot(time_support, wav_signal) + ax[0].set_title("Audio Signal") + ax[0].set_ylabel("Amplitude (Pa)") + ax[0].grid(True) + ax[1].plot(time_support, rpm_signal, color="red") + ax[1].set_title("RPM profile") + ax[1].set_ylabel("rpm") + ax[1].grid(True) + plt.xlabel("Time (s)") + plt.show() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_001.png + :alt: Audio Signal, RPM profile + :srcset: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 155-158 + +Plot spectrogram of the original signal +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Plot the spectrogram of the original signal. + +.. GENERATED FROM PYTHON SOURCE LINES 158-164 + +.. code-block:: Python + + + stft = Stft(signal=fc_signal[0], window_overlap=0.9, fft_size=8192) + stft.process() + max_stft = 20 * np.log10(np.max(stft.get_stft_magnitude_as_nparray())) + plot_stft(stft, max_stft) + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_002.png + :alt: STFT + :srcset: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_002.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 165-168 + +Isolate orders +~~~~~~~~~~~~~~~~ +Isolate orders 2, 4, and 6 with the ``IsolateOrders`` class. + +.. GENERATED FROM PYTHON SOURCE LINES 168-198 + +.. code-block:: Python + + + field_wav, field_rpm = wav_loader.get_output() + + # Define parameters for order isolation + field_wav.unit = "Pa" + order_to_isolate = [2, 4, 6] # Orders indexes to isolate as a list + fft_size = 8192 # FFT Size (in samples) + window_type = "HANN" # Window type + window_overlap = 0.9 # Window overlap + width_selection = 3 # Width of the order selection in Hz + + # Instantiate the ``IsolateOrders`` class with the parameters + isolate_orders = IsolateOrders( + signal=field_wav, + rpm_profile=field_rpm, + orders=order_to_isolate, + fft_size=fft_size, + window_type=window_type, + window_overlap=window_overlap, + width_selection=width_selection, + ) + + # Isolate orders + isolate_orders.process() + + # Plot the spectrogram of the isolated orders + stft.signal = isolate_orders.get_output() + stft.process() + plot_stft(stft, max_stft) + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_003.png + :alt: STFT + :srcset: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_003.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 199-202 + +Isolate different orders +~~~~~~~~~~~~~~~~~~~~~~~~ +Change FFT size, order indexes, and window type. Then re-isolate the orders. + +.. GENERATED FROM PYTHON SOURCE LINES 202-215 + +.. code-block:: Python + + + # Change some parameters directly using the setters of the class + isolate_orders.orders = [2, 6] + isolate_orders.window_type = "BLACKMAN" + + # Reprocess (Must be called explicitly. Otherwise, the output won't be updated.) + isolate_orders.process() + + + # Plot the spectrogram of the isolated orders + stft.signal = isolate_orders.get_output() + stft.process() + plot_stft(stft, max_stft) + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_004.png + :alt: STFT + :srcset: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_004.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 216-219 + +Work with the isolated signal +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Plot the signal containing the isolated orders and compute its loudness. + +.. GENERATED FROM PYTHON SOURCE LINES 219-240 + +.. code-block:: Python + + + # Plot the signal directly using the method from the ``IsolateOrders`` class + isolate_orders.plot() + + # Use the ``Loudness`` class to compute the loudness of the isolate signal + input_loudness = isolate_orders.get_output() + input_loudness.unit = "Pa" + loudness = LoudnessISO532_1_Stationary(signal=input_loudness) + loudness.process() + + loudness_isolated_signal = loudness.get_loudness_level_phon() + + # Compute the loudness for the original signal + loudness.signal = field_wav + loudness.process() + + loudness_original_signal = loudness.get_loudness_level_phon() + + print(f"Loudness of the original signal is {loudness_original_signal: .1f} phons.") + print(f"Loudness of the isolated signal is {loudness_isolated_signal: .1f} phons.") + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_005.png + :alt: 004 isolate orders + :srcset: /examples/gallery_examples/images/sphx_glr_004_isolate_orders_005.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Loudness of the original signal is 76.6 phons. + Loudness of the isolated signal is 61.4 phons. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 241-244 + +Isolate orders of several signals in a loop +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Loop over a list of given signals and write them as a WAV file. + +.. GENERATED FROM PYTHON SOURCE LINES 244-274 + +.. code-block:: Python + + + # Obtain parent folder of the 'accel_with_rpm.wav' file + parent_folder = pathlib.Path(path_accel_wav).parent.absolute() + + path_accel_wav_2 = download_accel_with_rpm_2_wav() + path_accel_wav_3 = download_accel_with_rpm_3_wav() + paths = (path_accel_wav, path_accel_wav_2, path_accel_wav_3) + + fft_sizes = [256, 2048, 4096] + + wav_writer = WriteWav() + + # Isolate orders for all the files containing RPM profiles in this folder + for file, fft_sz in zip(paths, fft_sizes): + # Loading the file + wav_loader.path_to_wav = file + wav_loader.process() + + # Set parameters for order isolation + isolate_orders.signal = wav_loader.get_output()[0] + isolate_orders.rpm_profile = wav_loader.get_output()[1] + isolate_orders.fft_size = fft_sz + isolate_orders.process() + + # Write as a WAV file on the disk + out_name = os.path.basename(file)[:-4] + "_isolated_fft_size_" + str(fft_sz) + ".wav" + path_to_write = parent_folder / out_name + wav_writer.path_to_write = str(path_to_write) + wav_writer.signal = isolate_orders.get_output() + wav_writer.process() + + + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (7 minutes 34.674 seconds) + + +.. _sphx_glr_download_examples_gallery_examples_004_isolate_orders.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: 004_isolate_orders.ipynb <004_isolate_orders.ipynb>` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: 004_isolate_orders.py <004_isolate_orders.py>` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/version/0.1/_sources/examples/gallery_examples/005_xtract_feature.rst.txt b/version/0.1/_sources/examples/gallery_examples/005_xtract_feature.rst.txt new file mode 100644 index 000000000..e1950a36c --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/005_xtract_feature.rst.txt @@ -0,0 +1,622 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples\gallery_examples\005_xtract_feature.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_gallery_examples_005_xtract_feature.py: + + +.. _xtract_feature_example: + +Use the Xtract feature +---------------------- + +This example shows how to use the Xtract feature in PyAnsys Sound. +It demonstrates different capabilities of this feature, such as +noise extraction, tonal extraction, and transient extraction. + +.. GENERATED FROM PYTHON SOURCE LINES 34-38 + +.. code-block:: Python + + + # Maximum frequency for STFT plots, change according to your need + MAX_FREQUENCY_PLOT_STFT = 5000.0 + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 39-43 + +Set up analysis +~~~~~~~~~~~~~~~ +Setting up the analysis consists of loading Ansys libraries, connecting to the +DPF server, and retrieving the example files. + +.. GENERATED FROM PYTHON SOURCE LINES 43-71 + +.. code-block:: Python + + + # Load Ansys libraries. + import os + + import matplotlib.pyplot as plt + import numpy as np + + from ansys.sound.core.examples_helpers import ( + download_xtract_demo_signal_1_wav, + download_xtract_demo_signal_2_wav, + ) + from ansys.sound.core.server_helpers import connect_to_or_start_server + from ansys.sound.core.signal_utilities import CropSignal, LoadWav + from ansys.sound.core.spectrogram_processing import Stft + from ansys.sound.core.xtract import ( + Xtract, + XtractDenoiser, + XtractDenoiserParameters, + XtractTonal, + XtractTonalParameters, + XtractTransient, + XtractTransientParameters, + ) + + # Connect to a remote server or start a local server + my_server = connect_to_or_start_server(use_license_context=True) + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 72-78 + +Define a custom function for STFT plots +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Define a custom function for STFT plots lets you have +more control over what you are displaying. +While you could use the ``Stft.plot()`` method, the custom function +defined here restricts the frequency range of the plot. + +.. GENERATED FROM PYTHON SOURCE LINES 78-133 + +.. code-block:: Python + + def plot_stft(stft_class, SPLmax, title="STFT", maximum_frequency=MAX_FREQUENCY_PLOT_STFT): + """Plot a short-term Fourier transform (STFT) into a figure window. + + Parameters + ---------- + stft_class: Stft + Object containing the STFT. + SPLmax: float + Maximum value (here in dB SPL) for the colormap. + title: str + Title of the figure. + maximum_frequency: float + Maximum frequency in Hz to display. + """ + out = stft_class.get_output_as_nparray() + + # Extract first half of the STFT (second half is symmetrical) + half_nfft = int(out.shape[0] / 2) + 1 + magnitude = stft_class.get_stft_magnitude_as_nparray() + + # Voluntarily ignore a numpy warning + np.seterr(divide="ignore") + magnitude = 20 * np.log10(magnitude[0:half_nfft, :]) + np.seterr(divide="warn") + + # Obtain sampling frequency, time steps, and number of time samples + fs = 1.0 / ( + stft_class.signal.time_freq_support.time_frequencies.data[1] + - stft_class.signal.time_freq_support.time_frequencies.data[0] + ) + time_step = np.floor(stft_class.fft_size * (1.0 - stft_class.window_overlap) + 0.5) / fs + num_time_index = len(stft_class.get_output().get_available_ids_for_label("time")) + + # Define boundaries of the plot + extent = [0, time_step * num_time_index, 0.0, fs / 2.0] + + # Plot + plt.figure() + plt.imshow( + magnitude, + origin="lower", + aspect="auto", + cmap="jet", + extent=extent, + vmax=SPLmax, + vmin=(SPLmax - 70.0), + ) + plt.colorbar(label="Magnitude (dB SPL)") + plt.ylabel("Frequency (Hz)") + plt.xlabel("Time (s)") + plt.ylim([0.0, maximum_frequency]) # Change the value of MAX_FREQUENCY_PLOT_STFT if needed + plt.title(title) + plt.show() + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 134-138 + +Load a demo signal for Xtract +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Load a demo signal from a WAV file using the ``LoadWav`` class. +The WAV file contains harmonics and shocks. + +.. GENERATED FROM PYTHON SOURCE LINES 138-163 + +.. code-block:: Python + + + # Return the input data of the example file + path_xtract_demo_signal_1 = download_xtract_demo_signal_1_wav() + + # Load the WAV file + wav_loader = LoadWav(path_to_wav=path_xtract_demo_signal_1) + wav_loader.process() + + # Plot the signal in time domain + time_domain_signal = wav_loader.get_output()[0] + time_vector = time_domain_signal.time_freq_support.time_frequencies.data + plt.plot(time_vector, time_domain_signal.data) + plt.title("Xtract Demo Signal 1") + plt.grid(True) + plt.xlabel("Time (s)") + plt.ylabel("Amplitude (Pa)") + plt.show() + + # Compute the spectrogram of the signal and plot it + stft_original = Stft(signal=wav_loader.get_output()[0], fft_size=1024, window_overlap=0.9) + stft_original.process() + max_stft = 20 * np.log10(np.max(stft_original.get_stft_magnitude_as_nparray())) + + plot_stft(stft_original, SPLmax=max_stft, maximum_frequency=20000.0) + + + + +.. rst-class:: sphx-glr-horizontal + + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_001.png + :alt: Xtract Demo Signal 1 + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_001.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_002.png + :alt: STFT + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_002.png + :class: sphx-glr-multi-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 164-167 + +Use individual extraction features +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The following topics show how to use different capabilities of Xtract independently. + +.. GENERATED FROM PYTHON SOURCE LINES 169-172 + +Noise extraction +^^^^^^^^^^^^^^^^ +The goal is to isolate a fan noise deprived of any tonal content in the demo signal. + +.. GENERATED FROM PYTHON SOURCE LINES 172-203 + +.. code-block:: Python + + + # Create a noise pattern using the first two seconds of the signal. + # First crop the first two seconds of the signal. + signal_cropper = CropSignal(signal=time_domain_signal, start_time=0.0, end_time=2.0) + signal_cropper.process() + cropped_signal = signal_cropper.get_output() + + # Then use the 'XtractDenoiserParameters' class to create the noise pattern. + xtract_denoiser_params = XtractDenoiserParameters() + xtract_denoiser_params.noise_psd = xtract_denoiser_params.create_noise_psd_from_noise_samples( + signal=cropped_signal, sampling_frequency=44100.0, window_length=100 + ) + + # Denoise the signal using the 'XtractDenoiser' class. + xtract_denoiser = XtractDenoiser( + input_signal=time_domain_signal, input_parameters=xtract_denoiser_params + ) + xtract_denoiser.process() + + noise_signal = xtract_denoiser.get_output()[1] + + # Plot the original signal and the noise signal in the same window + plt.plot(time_vector, time_domain_signal.data, label="Original Signal") + plt.plot(time_vector, noise_signal.data, label="Noise Signal") + plt.grid(True) + plt.xlabel("Time (s)") + plt.ylabel("Amplitude (Pa)") + plt.title("Original Signal and Noise Signal") + plt.legend() + plt.show() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_003.png + :alt: Original Signal and Noise Signal + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_003.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 204-207 + +Tone extraction +^^^^^^^^^^^^^^^ +The goal is to isolate the tones using the right settings. + +.. GENERATED FROM PYTHON SOURCE LINES 207-222 + +.. code-block:: Python + + + # Try a first attempt of tone extraction with a + # First set of parameters using the 'XtractTonalParameters' class. + xtract_tonal_params = XtractTonalParameters() + xtract_tonal_params.regularity = 1.0 + xtract_tonal_params.maximum_slope = 1000.0 + xtract_tonal_params.minimum_duration = 0.22 + xtract_tonal_params.intertonal_gap = 10.0 + xtract_tonal_params.local_emergence = 2.0 + xtract_tonal_params.fft_size = 2048 + + ## Now perform the tonal extraction using the 'XtractTonal' class + xtract_tonal = XtractTonal(input_signal=time_domain_signal, input_parameters=xtract_tonal_params) + xtract_tonal.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 223-224 + +Plot the spectrogram to assess the quality of the output. + +.. GENERATED FROM PYTHON SOURCE LINES 224-236 + +.. code-block:: Python + + stft_modified_signal = Stft(signal=xtract_tonal.get_output()[0], fft_size=1024, window_overlap=0.9) + stft_modified_signal.process() + + print("Plot of the spectrograms with tonal extraction parameters that do not work.") + + ## Spectrogram of the original signal + plot_stft(stft_original, SPLmax=max_stft, title="Original Signal") + + ## Spectrogram of the modified signal + plot_stft(stft_modified_signal, SPLmax=max_stft, title="Extracted Tones") + # You can see from the obtained plot that the tones are not properly extracted. + + + + +.. rst-class:: sphx-glr-horizontal + + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_004.png + :alt: Original Signal + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_004.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_005.png + :alt: Extracted Tones + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_005.png + :class: sphx-glr-multi-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Plot of the spectrograms with tonal extraction parameters that do not work. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 237-238 + +Try again with a different parameter for the maximum slope. + +.. GENERATED FROM PYTHON SOURCE LINES 238-250 + +.. code-block:: Python + + xtract_tonal_params.maximum_slope = 5000.0 + xtract_tonal.process() + + # Recheck the plots + print("Plot of the spectrograms with the right tonal extraction parameters.") + plot_stft(stft_original, SPLmax=max_stft, title="Original Signal") + + # Spectrogram of the modified signal + stft_modified_signal.signal = xtract_tonal.get_output()[0] + stft_modified_signal.process() + plot_stft(stft_modified_signal, SPLmax=max_stft, title="Extracted Tones") + + + + +.. rst-class:: sphx-glr-horizontal + + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_006.png + :alt: Original Signal + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_006.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_007.png + :alt: Extracted Tones + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_007.png + :class: sphx-glr-multi-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Plot of the spectrograms with the right tonal extraction parameters. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 251-259 + +Transient extraction +^^^^^^^^^^^^^^^^^^^^ +The goal is to isolate the transients using the right settings. +While these settings are not as easy to handle, they are well explained in the tutorial videos +installed with the Ansys Sound Analysis and Specification (SAS) standalone +application (with the user interface). You can also find the +`SAS - XTRACT transient `_ +videos on the Ansys Learning Hub. + +.. GENERATED FROM PYTHON SOURCE LINES 259-284 + +.. code-block:: Python + + + # Create a set of transient parameters. + # This example assumes that the best minimum and maximum thresholds are known. + # You can use the SAS interface to help set up these thresholds interactively. + xtract_transient_params = XtractTransientParameters(lower_threshold=51.5, upper_threshold=60.0) + + # Perform the transient extraction using the 'XtractTransient' class. + xtract_transient = XtractTransient( + input_signal=time_domain_signal, input_parameters=xtract_transient_params + ) + xtract_transient.process() + transient_signal = xtract_transient.get_output()[0] + + # Plot the original signal and the transient signal in the same window + plt.plot(time_vector, time_domain_signal.data, label="Original Signal", linewidth=0.1) + plt.plot(time_vector, transient_signal.data, label="Transient Signal", linewidth=0.1) + plt.grid(True) + plt.xlabel("Time (s)") + plt.ylabel("Amplitude (Pa)") + plt.title("Original Signal and Transient signal") + leg = plt.legend() + for line in leg.get_lines(): + line.set_linewidth(0.5) + plt.show() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_008.png + :alt: Original Signal and Transient signal + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_008.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 285-289 + +Use a combination of extraction features and loop on several signals +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The idea here is to loop over several signals and use the ``Xtract`` class to combine +all previous classes. + +.. GENERATED FROM PYTHON SOURCE LINES 289-354 + +.. code-block:: Python + + + + path_xtract_demo_signal_2 = download_xtract_demo_signal_2_wav() + + paths = [path_xtract_demo_signal_1, path_xtract_demo_signal_2] + + # Instantiate the 'Xtract' class with the parameters previously set + xtract = Xtract( + parameters_denoiser=xtract_denoiser_params, + parameters_tonal=xtract_tonal_params, + parameters_transient=xtract_transient_params, + ) + + # Loop over all signal paths contained in the 'paths' variable + for p in paths: + # Name the signal using the file name + signal_name = os.path.basename(p) + + # Load the signal + wav_loader.path_to_wav = p + wav_loader.process() + time_domain_signal = wav_loader.get_output()[0] + + # Plot the time domain signal + ylims = [-3.0, 3.0] + plt.figure() + plt.plot(time_vector, time_domain_signal.data, label="Original Signal") + plt.ylim(ylims) + plt.ylabel("Amplitude (Pa)") + plt.xlabel("Time (s)") + plt.grid() + plt.legend() + plt.title(signal_name) + plt.show() + + # Compute and plot the STFT + stft_original.signal = time_domain_signal + stft_original.process() + plot_stft(stft_class=stft_original, SPLmax=max_stft, title=f"STFT for signal {signal_name}") + + # Use Xtract with the loaded signal + xtract.input_signal = time_domain_signal + xtract.process() + + # Collect outputs and plot everything in one window + noise_signal, tonal_signal, transient_signal, remainder_signal = xtract.get_output() + + f, axs = plt.subplots(nrows=5) + axs[0].plot(time_vector, time_domain_signal.data, label="Original Signal", color="blue") + axs[1].plot(time_vector, noise_signal.data, label="Noise Signal", color="red") + axs[2].plot(time_vector, tonal_signal.data, label="Tonal Signal", color="green") + axs[2].set(ylabel="Amplitude (Pa)") # Set ylabel for middle plot only + axs[3].plot(time_vector, transient_signal.data, label="Transient Signal", color="purple") + axs[4].plot(time_vector, remainder_signal.data, label="Remainder Signal", color="black") + + for ax in axs: + ax.set_ylim(ylims) + ax.grid() + ax.legend() + ax.set_aspect("auto") + + plt.xlabel("Time (s)") + plt.legend() + plt.suptitle(f"Original and extracted signals for {signal_name}") + plt.show() + + + +.. rst-class:: sphx-glr-horizontal + + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_009.png + :alt: xtract_demo_signal_1.wav + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_009.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_010.png + :alt: STFT for signal xtract_demo_signal_1.wav + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_010.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_011.png + :alt: Original and extracted signals for xtract_demo_signal_1.wav + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_011.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_012.png + :alt: xtract_demo_signal_2.wav + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_012.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_013.png + :alt: STFT for signal xtract_demo_signal_2.wav + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_013.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_014.png + :alt: Original and extracted signals for xtract_demo_signal_2.wav + :srcset: /examples/gallery_examples/images/sphx_glr_005_xtract_feature_014.png + :class: sphx-glr-multi-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (12 minutes 43.565 seconds) + + +.. _sphx_glr_download_examples_gallery_examples_005_xtract_feature.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: 005_xtract_feature.ipynb <005_xtract_feature.ipynb>` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: 005_xtract_feature.py <005_xtract_feature.py>` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/version/0.1/_sources/examples/gallery_examples/006_calculate_PR_and_TNR.rst.txt b/version/0.1/_sources/examples/gallery_examples/006_calculate_PR_and_TNR.rst.txt new file mode 100644 index 000000000..b3831e177 --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/006_calculate_PR_and_TNR.rst.txt @@ -0,0 +1,482 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples\gallery_examples\006_calculate_PR_and_TNR.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_gallery_examples_006_calculate_PR_and_TNR.py: + + +.. _calculate_PR_and_TNR: + +Calculate TNR and PR +-------------------- + +This example shows how to calculate tone-to-noise ratio (TNR) and prominence +ratio (PR) following the ECMA 418-1 and ISO 7779 standards. It also extracts +the desired TNR and PR information. + +.. GENERATED FROM PYTHON SOURCE LINES 35-39 + +Set up analysis +~~~~~~~~~~~~~~~ +Setting up the analysis consists of loading Ansys libraries, connecting to the +DPF server, and retrieving the example files. + +.. GENERATED FROM PYTHON SOURCE LINES 39-53 + +.. code-block:: Python + + + # Load Ansys libraries. + from ansys.dpf.core import TimeFreqSupport, fields_factory, locations + import numpy as np + + from ansys.sound.core.examples_helpers import download_flute_psd, download_flute_wav + from ansys.sound.core.psychoacoustics import ProminenceRatio, ToneToNoiseRatio + from ansys.sound.core.server_helpers import connect_to_or_start_server + from ansys.sound.core.signal_utilities import LoadWav + from ansys.sound.core.spectral_processing import PowerSpectralDensity + + # Connect to a remote server or start a local server. + server = connect_to_or_start_server() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 54-58 + +Calculate TNR from a PSD +~~~~~~~~~~~~~~~~~~~~~~~~ +Load a power spectral density (PSD) stored as a text file and then use it to create +a field that serves as an input for the TNR calculation. + +.. GENERATED FROM PYTHON SOURCE LINES 58-96 + +.. code-block:: Python + + + # Load the PSD contained in an ASCII file. This file has two columns: 'Frequency (Hz)' + # and 'PSD amplitude (dB SPL/Hz)'. The data is located in + # "C:\Users\username\AppData\Local\Ansys\ansys_sound_core\examples\". + path_flute_psd = download_flute_psd() + fid = open(path_flute_psd) + fid.readline() # Skip the first line (header) + all_lines = fid.readlines() + fid.close() + + # Create the array of PSD amplitude values. + psd_dBSPL_per_Hz = [] + frequencies_original = [] + for line in all_lines: + splitted_line = line.split() + psd_dBSPL_per_Hz.append(float(splitted_line[1])) + frequencies_original.append(float(splitted_line[0])) + + # Convert amplitudes in dBSPL/Hz into power in Pa^2/Hz. + psd_dBSPL_per_Hz = np.array(psd_dBSPL_per_Hz) + psd_Pa2_per_Hz = np.power(10, psd_dBSPL_per_Hz / 10) * 4e-10 + + # The TNR/PR operators require the frequency array to be regularly spaced. + # Thus, the original frequencies are interpolated to regularly spaced points. + frequencies_interp = np.linspace(0, 22050, len(frequencies_original)) + psd_Pa2_per_Hz_interp = np.interp(frequencies_interp, frequencies_original, psd_Pa2_per_Hz) + + # Create the input PSD field for computation of TNR and PR. + f_psd = fields_factory.create_scalar_field(num_entities=1, location=locations.time_freq) + f_psd.append(psd_Pa2_per_Hz_interp, 1) + + # Create and include a field containing the array of frequencies. + support = TimeFreqSupport() + f_frequencies = fields_factory.create_scalar_field(num_entities=1, location=locations.time_freq) + f_frequencies.append(frequencies_interp, 1) + support.time_frequencies = f_frequencies + f_psd.time_freq_support = support + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 97-98 + +Create a ``ToneToNoiseRatio`` object, set the created PSD field as input, and compute the TNR. + +.. GENERATED FROM PYTHON SOURCE LINES 98-101 + +.. code-block:: Python + + tone_to_noise_ratio = ToneToNoiseRatio(psd=f_psd) + tone_to_noise_ratio.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 102-103 + +Print results. + +.. GENERATED FROM PYTHON SOURCE LINES 103-119 + +.. code-block:: Python + + number_tones = tone_to_noise_ratio.get_nb_tones() + TNR = tone_to_noise_ratio.get_max_TNR_value() + TNR_frequencies = tone_to_noise_ratio.get_peaks_frequencies() + TNR_values = tone_to_noise_ratio.get_TNR_values() + TNR_levels = tone_to_noise_ratio.get_peaks_levels() + + print( + f"\n" + f"Number of tones found: {number_tones}\n" + f"Maximum TNR value: {np.round(TNR, 1)} dB\n" + f"All detected peaks' frequencies (Hz): " + f"{np.round(TNR_frequencies)}\n" + f"All peaks' TNR values (dB): {np.round(TNR_values, 1)}\n" + f"All peaks' absolute levels (dB SPL): {np.round(TNR_levels, 1)}\n" + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Number of tones found: 11 + Maximum TNR value: 38.0 dB + All detected peaks' frequencies (Hz): [ 261. 525. 786. 1047. 1311. 1572. 1836. 2097. 2361. 2632. 2888.] + All peaks' TNR values (dB): [38. 37.8 34.4 29.5 22.4 25.9 32.7 18. 9.7 10.4 0.3] + All peaks' absolute levels (dB SPL): [71.1 79.3 76.9 68.2 62. 68.6 72.6 63.1 55.2 52.8 44.5] + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 120-121 + +Plot the TNR over frequency. + +.. GENERATED FROM PYTHON SOURCE LINES 121-123 + +.. code-block:: Python + + tone_to_noise_ratio.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_006_calculate_PR_and_TNR_001.png + :alt: Tone-to-noise ratio + :srcset: /examples/gallery_examples/images/sphx_glr_006_calculate_PR_and_TNR_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 124-125 + +Recalculate the TNR for specific frequencies. + +.. GENERATED FROM PYTHON SOURCE LINES 125-129 + +.. code-block:: Python + + frequencies_i = [261, 525, 786, 1836] + tone_to_noise_ratio = ToneToNoiseRatio(psd=f_psd, frequency_list=frequencies_i) + tone_to_noise_ratio.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 130-131 + +Print information for a specific detected peak. + +.. GENERATED FROM PYTHON SOURCE LINES 131-143 + +.. code-block:: Python + + tone_to_noise_ratio_525 = tone_to_noise_ratio.get_single_tone_info(tone_index=1) + TNR_frequency = tone_to_noise_ratio_525[0] + TNR_width = tone_to_noise_ratio_525[4] - tone_to_noise_ratio_525[3] + TNR = tone_to_noise_ratio_525[1] + print( + f"\n" + f"TNR info for peak at ~525 Hz: \n" + f"Exact tone frequency: {round(TNR_frequency, 2)} Hz\n" + f"Tone width: {round(TNR_width, 2)} Hz\n" + f"TNR value: {round(TNR, 2)} dB\n\n" + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + TNR info for peak at ~525 Hz: + Exact tone frequency: 524.87 Hz + Tone width: 48.45 Hz + TNR value: 37.84 dB + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 144-147 + +Calculate PR from a PSD +~~~~~~~~~~~~~~~~~~~~~~~ +Use the PowerSpectralDensity class to calculate a PSD, and compute Prominence Ratio (PR). + +.. GENERATED FROM PYTHON SOURCE LINES 147-154 + +.. code-block:: Python + + + # Load example data from WAV file. + path_flute_wav = download_flute_wav() + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + flute_signal = wav_loader.get_output()[0] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 155-156 + +Create a PowerSpectralDensity object, set its input signal and parameters, and compute the PSD. + +.. GENERATED FROM PYTHON SOURCE LINES 156-161 + +.. code-block:: Python + + psd_object = PowerSpectralDensity( + flute_signal, fft_size=8192, window_type="HANN", window_length=8192, overlap=0.8 + ) + psd_object.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 162-163 + +Get the computed PSD as a Field. + +.. GENERATED FROM PYTHON SOURCE LINES 163-165 + +.. code-block:: Python + + f_psd = psd_object.get_output() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 166-167 + +Create a ProminenceRatio object, set the computed PSD as input, and compute the PR. + +.. GENERATED FROM PYTHON SOURCE LINES 167-170 + +.. code-block:: Python + + prominence_ratio = ProminenceRatio(psd=f_psd) + prominence_ratio.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 171-172 + +Print the results. + +.. GENERATED FROM PYTHON SOURCE LINES 172-186 + +.. code-block:: Python + + number_tones = prominence_ratio.get_nb_tones() + PR = prominence_ratio.get_max_PR_value() + PR_frequencies = prominence_ratio.get_peaks_frequencies() + PR_values = prominence_ratio.get_PR_values() + PR_levels = prominence_ratio.get_peaks_levels() + print( + f"\n" + f"Number of tones found: {number_tones}\n" + f"Maximum PR value: {np.round(PR, 1)} dB\n" + f"All detected peaks' frequencies (Hz): {np.round(PR_frequencies)}\n" + f"All peaks' PR values (dB): {np.round(PR_values, 1)}\n" + f"All peaks' absolute levels (dB SPL): {np.round(PR_levels, 1)}\n" + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Number of tones found: 9 + Maximum PR value: 44.6 dB + All detected peaks' frequencies (Hz): [ 264. 522. 786. 1050. 1836. 3666. 3930. 6029. 6288.] + All peaks' PR values (dB): [38.5 44.6 40.4 9.2 6. 2.7 2.6 0.5 0.5] + All peaks' absolute levels (dB SPL): [70.9 79.1 76.7 67.9 72.4 45. 42.2 32.6 35.1] + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 187-188 + +Plot the PR as a function of frequency. + +.. GENERATED FROM PYTHON SOURCE LINES 188-190 + +.. code-block:: Python + + prominence_ratio.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_006_calculate_PR_and_TNR_002.png + :alt: Prominence Ratio + :srcset: /examples/gallery_examples/images/sphx_glr_006_calculate_PR_and_TNR_002.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 191-192 + +Recalculate the PR for specific frequencies. + +.. GENERATED FROM PYTHON SOURCE LINES 192-196 + +.. code-block:: Python + + frequencies_i = [261, 525, 786, 1836] + prominence_ratio = ProminenceRatio(psd=f_psd, frequency_list=frequencies_i) + prominence_ratio.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 197-198 + +Print information for a specific detected peak. + +.. GENERATED FROM PYTHON SOURCE LINES 198-209 + +.. code-block:: Python + + prominence_ratio_786 = prominence_ratio.get_single_tone_info(tone_index=2) + PR_frequency = prominence_ratio_786[0] + PR_width = prominence_ratio_786[4] - prominence_ratio_786[3] + PR = prominence_ratio_786[1] + print( + f"\n" + f"PR info for peak at ~786 Hz: \n" + f"Exact tone frequency: {round(PR_frequency, 2)} Hz\n" + f"Tone width: {round(PR_width, 2)} Hz\n" + f"PR value: {round(PR, 2)} dB\n" + ) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + PR info for peak at ~786 Hz: + Exact tone frequency: 785.96 Hz + Tone width: 75.37 Hz + PR value: 40.4 dB + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 3.953 seconds) + + +.. _sphx_glr_download_examples_gallery_examples_006_calculate_PR_and_TNR.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: 006_calculate_PR_and_TNR.ipynb <006_calculate_PR_and_TNR.ipynb>` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: 006_calculate_PR_and_TNR.py <006_calculate_PR_and_TNR.py>` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/version/0.1/_sources/examples/gallery_examples/007_calculate_psychoacoustic_indicators.rst.txt b/version/0.1/_sources/examples/gallery_examples/007_calculate_psychoacoustic_indicators.rst.txt new file mode 100644 index 000000000..e57fb058f --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/007_calculate_psychoacoustic_indicators.rst.txt @@ -0,0 +1,512 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples\gallery_examples\007_calculate_psychoacoustic_indicators.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_gallery_examples_007_calculate_psychoacoustic_indicators.py: + + +.. _calculate_psychoacoustic_indicators: + +Calculate psychoacoustic indicators +----------------------------------- + +This example shows how to calculate psychoacoustic indicators. +The following indicators are included: + +- Loudness of stationary sounds according to ISO 532-1. +- Loudness of time-varying sounds according to ISO 532-1. +- Sharpness according to Zwicker and Fastl, "Psychoacoustics: Facts and models", 1990. +- Roughness according to Daniel and Weber, "Psychoacoustical Roughness: Implementation of an + Optimized Model, 1997. +- Fluctuation strength according to Sontacchi, "Entwicklung eines Modulkonzeptes für die + psychoakustische Geräuschanalyse under MatLab Diplomarbeit", 1998. + +The example shows how to perform these operations: + +- Set up the analysis. +- Calculate indicators on loaded WAV files. +- Get calculation outputs. +- Plot some corresponding curves. + +.. GENERATED FROM PYTHON SOURCE LINES 50-54 + +Set up analysis +~~~~~~~~~~~~~~~ +Setting up the analysis consists of loading Ansys libraries, connecting to the +DPF server, and retrieving the example files. + +.. GENERATED FROM PYTHON SOURCE LINES 54-81 + +.. code-block:: Python + + + # Load Ansys libraries. + import os + + import numpy as np + + from ansys.sound.core.examples_helpers import ( + download_accel_with_rpm_wav, + download_flute_2_wav, + download_flute_wav, + ) + from ansys.sound.core.psychoacoustics import ( + FluctuationStrength, + LoudnessISO532_1_Stationary, + LoudnessISO532_1_TimeVarying, + Roughness, + Sharpness, + ) + from ansys.sound.core.psychoacoustics.roughness import Roughness + from ansys.sound.core.psychoacoustics.sharpness import Sharpness + from ansys.sound.core.server_helpers import connect_to_or_start_server + from ansys.sound.core.signal_utilities import LoadWav + + # Connect to a remote server or start a local server. + server = connect_to_or_start_server() + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 82-88 + +Calculate ISO 532-1 loudness for a stationary sound +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Load a signal from a WAV file using the ``LoadWav`` class. It is returned as a DPF +field container. For more information, see `fields_container +`_ +in the DPF-Core API documentation. + +.. GENERATED FROM PYTHON SOURCE LINES 88-95 + +.. code-block:: Python + + + # Load example data from WAV file + path_flute_wav = download_flute_wav() + wav_loader = LoadWav(path_flute_wav) + wav_loader.process() + fc_signal = wav_loader.get_output() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 96-97 + +Create a ``LoudnessISO532_1_Stationary`` object, set its signal, and compute the loudness. + +.. GENERATED FROM PYTHON SOURCE LINES 97-100 + +.. code-block:: Python + + loudness_stationary = LoudnessISO532_1_Stationary(signal=fc_signal) + loudness_stationary.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 101-102 + +Get the value in sone or in phon. + +.. GENERATED FROM PYTHON SOURCE LINES 102-111 + +.. code-block:: Python + + loudness_sone = loudness_stationary.get_loudness_sone() + loudness_level_phon = loudness_stationary.get_loudness_level_phon() + file_name = os.path.basename(path_flute_wav) + print( + f"\nThe loudness of sound file {file_name} " + f"is{loudness_sone: .1f} sones " + f"or{loudness_level_phon: .1f} phons." + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + The loudness of sound file flute.wav is 39.6 sones or 93.1 phons. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 112-113 + +Plot the specific loudness. + +.. GENERATED FROM PYTHON SOURCE LINES 113-115 + +.. code-block:: Python + + loudness_stationary.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_007_calculate_psychoacoustic_indicators_001.png + :alt: Specific loudness + :srcset: /examples/gallery_examples/images/sphx_glr_007_calculate_psychoacoustic_indicators_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 116-119 + +Calculate ISO 532-1 loudness for several signals at once +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Load another WAV file and store it along with the first one. + +.. GENERATED FROM PYTHON SOURCE LINES 119-129 + +.. code-block:: Python + + + path_flute2_wav = download_flute_2_wav() + wav_loader = LoadWav(path_flute2_wav) + wav_loader.process() + + # Store the second signal as a second field in the DPF fields container. + fc_two_signals = fc_signal + fc_two_signals.add_field({"channel_number": 1}, wav_loader.get_output()[0]) + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 130-131 + +Calculate the loudness for both signals at once. + +.. GENERATED FROM PYTHON SOURCE LINES 131-134 + +.. code-block:: Python + + loudness_stationary = LoudnessISO532_1_Stationary(signal=fc_two_signals) + loudness_stationary.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 135-136 + +Get the values in sone or in phon. + +.. GENERATED FROM PYTHON SOURCE LINES 136-145 + +.. code-block:: Python + + loudness_sone2 = loudness_stationary.get_loudness_sone(1) + loudness_level_phon2 = loudness_stationary.get_loudness_level_phon(1) + file_name2 = os.path.basename(path_flute2_wav) + print( + f"In comparison, the loudness of sound file {file_name2} " + f"is{loudness_sone2: .1f} sones " + f"or{loudness_level_phon2: .1f} phons." + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + In comparison, the loudness of sound file flute2.wav is 16.2 sones or 80.2 phons. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 146-148 + +Plot specific loudness for both signals in a single figure. Note how the first sound has a +higher specific loudness than the second. + +.. GENERATED FROM PYTHON SOURCE LINES 148-150 + +.. code-block:: Python + + loudness_stationary.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_007_calculate_psychoacoustic_indicators_002.png + :alt: Specific loudness + :srcset: /examples/gallery_examples/images/sphx_glr_007_calculate_psychoacoustic_indicators_002.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 151-154 + +Calculate ISO 532-1 loudness for a non-stationary sound +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Load a new signal (non-stationary) from a WAV file. + +.. GENERATED FROM PYTHON SOURCE LINES 154-159 + +.. code-block:: Python + + path_accel_wav = download_accel_with_rpm_wav() + wav_loader = LoadWav(path_accel_wav) + wav_loader.process() + f_signal = wav_loader.get_output()[0] # Field 0 only, because the RPM profile is useless here. + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 160-161 + +Create a ``LoudnessISO532_1_TimeVarying`` object, set its signal, and compute the loudness. + +.. GENERATED FROM PYTHON SOURCE LINES 161-164 + +.. code-block:: Python + + loudness_time_varying = LoudnessISO532_1_TimeVarying(signal=f_signal) + loudness_time_varying.process() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 165-166 + +Get percentile loudness values. + +.. GENERATED FROM PYTHON SOURCE LINES 166-180 + +.. code-block:: Python + + N5 = loudness_time_varying.get_N5_sone() + N10 = loudness_time_varying.get_N10_sone() + L5 = loudness_time_varying.get_L5_phon() + L10 = loudness_time_varying.get_L10_phon() + + file_name3 = os.path.basename(path_accel_wav) + print( + f"\nThe sound file {file_name3} has the following percentile loudness values: \n" + f"- N5 = {np.round(N5, 1)} sones.\n" + f"- N10 = {np.round(N10, 1)} sones.\n" + f"- L5 = {np.round(L5, 1)} phons.\n" + f"- L10 = {np.round(L10, 1)} phons." + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + The sound file accel_with_rpm.wav has the following percentile loudness values: + - N5 = 16.4 sones. + - N10 = 15.6 sones. + - L5 = 80.4 phons. + - L10 = 79.6 phons. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 181-182 + +Plot loudness as a function of time. + +.. GENERATED FROM PYTHON SOURCE LINES 182-184 + +.. code-block:: Python + + loudness_time_varying.plot() + + + + +.. image-sg:: /examples/gallery_examples/images/sphx_glr_007_calculate_psychoacoustic_indicators_003.png + :alt: Time-varying loudness, Time-varying loudness level + :srcset: /examples/gallery_examples/images/sphx_glr_007_calculate_psychoacoustic_indicators_003.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 185-188 + +Calculate sharpness, roughness, and fluctuation strength +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Calculate sharpness, roughness, and fluctuation strength for the two sounds. + +.. GENERATED FROM PYTHON SOURCE LINES 190-191 + +Calculate the sharpness. + +.. GENERATED FROM PYTHON SOURCE LINES 191-195 + +.. code-block:: Python + + sharpness = Sharpness(signal=fc_two_signals) + sharpness.process() + sharpness_values = (sharpness.get_sharpness(0), sharpness.get_sharpness(1)) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 196-197 + +Calculate the roughness. + +.. GENERATED FROM PYTHON SOURCE LINES 197-201 + +.. code-block:: Python + + roughness = Roughness(signal=fc_two_signals) + roughness.process() + roughness_values = (roughness.get_roughness(0), roughness.get_roughness(1)) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 202-203 + +Calculate the fluctuation strength. + +.. GENERATED FROM PYTHON SOURCE LINES 203-210 + +.. code-block:: Python + + fluctuation_strength = FluctuationStrength(signal=fc_two_signals) + fluctuation_strength.process() + fluctuation_strength_values = ( + fluctuation_strength.get_fluctuation_strength(0), + fluctuation_strength.get_fluctuation_strength(1), + ) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 211-212 + +Print the results. + +.. GENERATED FROM PYTHON SOURCE LINES 212-222 + +.. code-block:: Python + + print( + f"\nThe sharpness of sound file {file_name} " + f"is{sharpness_values[0]: .2f} acum, " + f"its roughness is{roughness_values[0]: .2f} asper, " + f"and its fluctuation strength is{fluctuation_strength_values[0]: .2f} vacil.\n" + f"For sound file {file_name2}, these indicators' values are, respectively, " + f"{sharpness_values[1]: .2f} acum, " + f"{roughness_values[1]: .2f} asper, " + f"and{fluctuation_strength_values[1]: .2f} vacil.\n" + ) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + The sharpness of sound file flute.wav is 1.19 acum, its roughness is 0.06 asper, and its fluctuation strength is 0.60 vacil. + For sound file flute2.wav, these indicators' values are, respectively, 1.15 acum, 0.03 asper, and 0.45 vacil. + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 52.138 seconds) + + +.. _sphx_glr_download_examples_gallery_examples_007_calculate_psychoacoustic_indicators.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: 007_calculate_psychoacoustic_indicators.ipynb <007_calculate_psychoacoustic_indicators.ipynb>` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: 007_calculate_psychoacoustic_indicators.py <007_calculate_psychoacoustic_indicators.py>` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/version/0.1/_sources/examples/gallery_examples/sg_execution_times.rst.txt b/version/0.1/_sources/examples/gallery_examples/sg_execution_times.rst.txt new file mode 100644 index 000000000..cb5471ecc --- /dev/null +++ b/version/0.1/_sources/examples/gallery_examples/sg_execution_times.rst.txt @@ -0,0 +1,55 @@ + +:orphan: + +.. _sphx_glr_examples_gallery_examples_sg_execution_times: + + +Computation times +================= +**23:05.853** total execution time for 7 files **from examples\gallery_examples**: + +.. container:: + + .. raw:: html + + + + + + + + .. list-table:: + :header-rows: 1 + :class: table table-striped sg-datatable + + * - Example + - Time + - Mem (MB) + * - :ref:`sphx_glr_examples_gallery_examples_005_xtract_feature.py` (``005_xtract_feature.py``) + - 12:43.565 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_004_isolate_orders.py` (``004_isolate_orders.py``) + - 07:34.674 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_003_compute_stft.py` (``003_compute_stft.py``) + - 01:20.899 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_007_calculate_psychoacoustic_indicators.py` (``007_calculate_psychoacoustic_indicators.py``) + - 00:52.138 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_001_initialize_server_and_deal_with_license.py` (``001_initialize_server_and_deal_with_license.py``) + - 00:29.813 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_006_calculate_PR_and_TNR.py` (``006_calculate_PR_and_TNR.py``) + - 00:03.953 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_002_load_resample_amplify_write_wav_files.py` (``002_load_resample_amplify_write_wav_files.py``) + - 00:00.810 + - 0.0 diff --git a/version/0.1/_sources/examples/index.rst.txt b/version/0.1/_sources/examples/index.rst.txt new file mode 100644 index 000000000..a0bbfc4bd --- /dev/null +++ b/version/0.1/_sources/examples/index.rst.txt @@ -0,0 +1,9 @@ +.. _ref_examples: + +.. === EXAMPLES Gallery === + +.. + We have to include this rather than include it in a tree. + +.. include:: gallery_examples/index.rst + :start-line: 2 \ No newline at end of file diff --git a/version/0.1/_sources/getting_started.rst.txt b/version/0.1/_sources/getting_started.rst.txt new file mode 100644 index 000000000..dab15a975 --- /dev/null +++ b/version/0.1/_sources/getting_started.rst.txt @@ -0,0 +1,98 @@ +=============== +Getting started +=============== + +This section explains how to install PyAnsys Sound (``ansys-sound-core``) in user mode. +If you are interested in contributing to PyAnsys Sound, see :ref:`ref_contribute` for +information on installing in developer mode. + +.. _prerequisistes: + +Prerequisites +------------- + +PyAnsys Sound supports Ansys 2024 R2 and later. Make sure that you have a licensed copy of +Ansys installed. To see which ``ansys-sound-core`` version corresponds to which Ansys version, +see :ref:`Compatibility`. + +PyAnsys Sound relies on these Ansys products: + +- `PyDPF-Core`_ +- DPF Sound, which is a plugin for DPF + +To install these prerequisites, you can use one of these methods: + +- From the `Download Center`_ on the Ansys Customer Portal, select the appropriate release + and then click the **Ansys Automated Installer** button. Use the downloaded installer to + install Ansys and Ansys Sound Analysis and Specification (SAS). Then, perform the steps in + `Install PyDPF-Core`_ in the PyDPF-Core documentation. +- From the DPF pre-release, download and install DPF Server and DPF Sound as described + in the pre-release guidelines in `Install DPF Server`_ in the PyDPF-Core documentation. + +.. note:: + You can also use a `DPF Server Docker image`_. + +Installation +------------ + +To install the latest ``ansys-sound-core`` package with ``pip``, run this command: + +.. code:: + + pip install ansys-sound-core + +To install a specific package version, specify the version in the ``pip`` command. For example, Ansys 2024 R2 +requires version 0.1.0 of the ``ansys-sound-core`` package. To install version 0.1.0, you would run this command: + +.. code:: + + pip install ansys-sound-core==0.1.0 + +You should use a `virtual environment `_ +because it keeps Python packages isolated from your Python system. + + +.. _Compatibility: + +Compatibility +------------- + +The following table shows which ``ansys-sound-core`` version is compatible with which DPF Server +version (Ansys version). + +By default, a DPF server is started from the latest installed Ansys version. + +.. list-table:: + :widths: 20 20 + :header-rows: 1 + + * - DPF Server version + - ansys.sound.core version + * - 8.0 (Ansys 2024 R2 pre0) + - 0.1.0 and later + + +Examples +-------- + +Comprehensive information on using PyAnsys Sound is available in :doc:`examples/index`. +At the end of each example, a button provides for downloading the example's Python source code. + + +.. _DPF Server Docker image: + +DPF Server Docker image +----------------------- + +To use a DPF Server Docker image, follow the steps in `Run DPF Server in a Docker container +`_ +in the PyDPF-Core documentation. Make sure that you also download the DPF Sound plugin for the corresponding release, +such as ``ansys_dpf_sound_win_v2024.1.pre0.zip``. After following these steps, you should have a running DPF Docker +container that listens on port 6780. + +.. LINKS AND REFERENCES +.. _PyDPF-Core: https://dpf.docs.pyansys.com/version/stable/ +.. _Ansys Sound: https://www.ansys.com/sound +.. _Download Center: https://download.ansys.com/Current%20Release +.. _Install PyDPF-Core: https://dpf.docs.pyansys.com/version/stable/getting_started/index.html#install-pydpf-core +.. _Install DPF Server: https://dpf.docs.pyansys.com/version/stable/getting_started/dpf_server.html#install-dpf-server \ No newline at end of file diff --git a/version/0.1/_sources/index.rst.txt b/version/0.1/_sources/index.rst.txt new file mode 100644 index 000000000..db9c1a113 --- /dev/null +++ b/version/0.1/_sources/index.rst.txt @@ -0,0 +1,80 @@ +PyAnsys Sound +============= + +.. toctree:: + :hidden: + :maxdepth: 3 + + getting_started + user_guide + api/index + examples/index + contribute + + +PyAnsys Sound lets you use the main features of `Ansys Sound`_ to perform +the postprocessing and analysis of sounds and vibrations in Python. This library is based on +`PyDPF-Core`_ and the DPF Sound plugin. It is a Python wrapper that implements classes on top +of DPF Sound operators. + +These documentation sections provide information on installing and using PyAnsys Sound: + +.. grid:: 1 1 2 2 + :gutter: 2 + + .. grid-item-card:: Getting started :material-regular:`directions_run` + :link: getting_started + :link-type: doc + + Learn how to install PyAnsys Sound and its prerequisites in user mode. + + .. grid-item-card:: User guide :material-regular:`menu_book` + :link: user_guide + :link-type: doc + + Learn how to start a DPF server, load a signal from a WAV files, and perform operations + on the signal. + + .. grid-item-card:: API reference :material-regular:`bookmark` + :link: api/index + :link-type: doc + + Understand how to use Python to interact programmatically with PyAnsys Sound. + + .. grid-item-card:: Examples :material-regular:`play_arrow` + :link: examples/index + :link-type: doc + + Explore examples that show how to use PyAnsys Sound for various workflows. + + + .. grid-item-card:: Contribute :material-regular:`group` + :link: contribute + :link-type: doc + + Learn how to contribute to the PyAnsys Sound codebase or documentation. + + +Key features +------------ + +The following API reference pages describe some key features of PyAnsys Sound: + +* :doc:`api/signal_utilities`: Tools to read and write WAV files in the format used by Ansys Sound Analysis + and Specification (SAS) and tools to perform basic editing of audio signals. +* :doc:`api/spectrogram_processing`: Time-frequency and time-representation conversion tools and time-frequency editing tools. +* :doc:`api/psychoacoustics`: Psychoacoustic indicators computation to measure perceived sound quality. +* :doc:`api/xtract`: Separation of a signal into several components, such as tonal, transient, and noise parts. + +Not all features of Ansys SAS are available in PyAnsys Sound. Features are regularly +added in new versions. + +.. _limitations: + +.. Limitations +.. ''''''''''' +.. - ??? + +.. LINKS AND REFERENCES +.. _Ansys Sound: https://www.ansys.com/sound +.. _PyDPF-Core: https://dpf.docs.pyansys.com/version/stable/ diff --git a/version/0.1/_sources/sg_execution_times.rst.txt b/version/0.1/_sources/sg_execution_times.rst.txt new file mode 100644 index 000000000..8cbbb200f --- /dev/null +++ b/version/0.1/_sources/sg_execution_times.rst.txt @@ -0,0 +1,55 @@ + +:orphan: + +.. _sphx_glr_sg_execution_times: + + +Computation times +================= +**23:05.853** total execution time for 7 files **from all galleries**: + +.. container:: + + .. raw:: html + + + + + + + + .. list-table:: + :header-rows: 1 + :class: table table-striped sg-datatable + + * - Example + - Time + - Mem (MB) + * - :ref:`sphx_glr_examples_gallery_examples_005_xtract_feature.py` (``..\..\examples\005_xtract_feature.py``) + - 12:43.565 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_004_isolate_orders.py` (``..\..\examples\004_isolate_orders.py``) + - 07:34.674 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_003_compute_stft.py` (``..\..\examples\003_compute_stft.py``) + - 01:20.899 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_007_calculate_psychoacoustic_indicators.py` (``..\..\examples\007_calculate_psychoacoustic_indicators.py``) + - 00:52.138 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_001_initialize_server_and_deal_with_license.py` (``..\..\examples\001_initialize_server_and_deal_with_license.py``) + - 00:29.813 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_006_calculate_PR_and_TNR.py` (``..\..\examples\006_calculate_PR_and_TNR.py``) + - 00:03.953 + - 0.0 + * - :ref:`sphx_glr_examples_gallery_examples_002_load_resample_amplify_write_wav_files.py` (``..\..\examples\002_load_resample_amplify_write_wav_files.py``) + - 00:00.810 + - 0.0 diff --git a/version/0.1/_sources/user_guide.rst.txt b/version/0.1/_sources/user_guide.rst.txt new file mode 100644 index 000000000..9558318d2 --- /dev/null +++ b/version/0.1/_sources/user_guide.rst.txt @@ -0,0 +1,67 @@ +========== +User guide +========== + +This section explains how to start a DPF server, load a signal from a WAV file, and perform operations +on this signal. + +Start a DPF server +------------------ + +You use the :func:`connect_to_or_start_server() ` +function to start either a remote or local DPF server, which is required to run PyAnsys Sound. + +You can start the server with this code: + +.. code:: python + + from ansys.sound.core.server_helpers import connect_to_or_start_server + + my_server = connect_to_or_start_server() + +If the ``ANSRV_DPF_SOUND_PORT`` environment variable is set, PyAnsys Sound +attempts to connect to a server located in a Docker container. The default port is ``6780``. + +If this environment variable is not set, PyAnsys Sound tries to start a local server. + +For more information on local and remote DPF servers, see `Install DPF Server`_ in the PyDPF-Core documentation. + +Load a signal +------------- + +Most of the processing done by PyAnsys Sound relies on temporal sound signals that are saved as WAV files. + +To load a WAV file, you must use the :class:`LoadWav ` class. +Once your signal is loaded, you can use all other PyAnsys Sound classes on this signal. + +.. code:: python + + from ansys.sound.core.signal_utilities import LoadWav + + # Load a WAV file and plot it + wav_loader = LoadWav(r"C:\path\to\my\wav\file.wav) + wav_loader.process() + + # Obtain the output as a DPF fields container + fc_wav_signal = wav_loader.get_output() + + # Plot the signal + wav_loader.plot() + +For descriptions of all PyAnsys Sound classes and helpers, see :doc:`api/index`. These classes +have four methods in common: + +- ``process()``: Performs the operation that the class was made for. This method must be called explicitly + every time an input parameter is changed. +- ``plot()``: Plots the output of the class. Depending on the nature of the output, the plot might be different. +- ``get_output()``: Gets the outputs as a DPF object (either a ``Field`` or a ``FieldsContainer`` object). +- ``get_output_as_nparray()``: Gets the output as a NumPy array. + +A class might also have some additional methods. + +For comprehensive information on using PyAnsys Sound classes and helpers, see :doc:`examples/index`. + +.. LINKS AND REFERENCES +.. _Ansys DPF: https://dpf.docs.pyansys.com/version/stable/ +.. _Ansys Sound: https://www.ansys.com/sound +.. _Install DPF Server: https://dpf.docs.pyansys.com/version/stable/getting_started/dpf_server.html#install-dpf-server \ No newline at end of file diff --git a/version/0.1/_sphinx_design_static/design-tabs.js b/version/0.1/_sphinx_design_static/design-tabs.js new file mode 100644 index 000000000..b25bd6a4f --- /dev/null +++ b/version/0.1/_sphinx_design_static/design-tabs.js @@ -0,0 +1,101 @@ +// @ts-check + +// Extra JS capability for selected tabs to be synced +// The selection is stored in local storage so that it persists across page loads. + +/** + * @type {Record} + */ +let sd_id_to_elements = {}; +const storageKeyPrefix = "sphinx-design-tab-id-"; + +/** + * Create a key for a tab element. + * @param {HTMLElement} el - The tab element. + * @returns {[string, string, string] | null} - The key. + * + */ +function create_key(el) { + let syncId = el.getAttribute("data-sync-id"); + let syncGroup = el.getAttribute("data-sync-group"); + if (!syncId || !syncGroup) return null; + return [syncGroup, syncId, syncGroup + "--" + syncId]; +} + +/** + * Initialize the tab selection. + * + */ +function ready() { + // Find all tabs with sync data + + /** @type {string[]} */ + let groups = []; + + document.querySelectorAll(".sd-tab-label").forEach((label) => { + if (label instanceof HTMLElement) { + let data = create_key(label); + if (data) { + let [group, id, key] = data; + + // add click event listener + // @ts-ignore + label.onclick = onSDLabelClick; + + // store map of key to elements + if (!sd_id_to_elements[key]) { + sd_id_to_elements[key] = []; + } + sd_id_to_elements[key].push(label); + + if (groups.indexOf(group) === -1) { + groups.push(group); + // Check if a specific tab has been selected via URL parameter + const tabParam = new URLSearchParams(window.location.search).get( + group + ); + if (tabParam) { + console.log( + "sphinx-design: Selecting tab id for group '" + + group + + "' from URL parameter: " + + tabParam + ); + window.sessionStorage.setItem(storageKeyPrefix + group, tabParam); + } + } + + // Check is a specific tab has been selected previously + let previousId = window.sessionStorage.getItem( + storageKeyPrefix + group + ); + if (previousId === id) { + // console.log( + // "sphinx-design: Selecting tab from session storage: " + id + // ); + // @ts-ignore + label.previousElementSibling.checked = true; + } + } + } + }); +} + +/** + * Activate other tabs with the same sync id. + * + * @this {HTMLElement} - The element that was clicked. + */ +function onSDLabelClick() { + let data = create_key(this); + if (!data) return; + let [group, id, key] = data; + for (const label of sd_id_to_elements[key]) { + if (label === this) continue; + // @ts-ignore + label.previousElementSibling.checked = true; + } + window.sessionStorage.setItem(storageKeyPrefix + group, id); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/version/0.1/_sphinx_design_static/sphinx-design.min.css b/version/0.1/_sphinx_design_static/sphinx-design.min.css new file mode 100644 index 000000000..a325746f2 --- /dev/null +++ b/version/0.1/_sphinx_design_static/sphinx-design.min.css @@ -0,0 +1 @@ +.sd-bg-primary{background-color:var(--sd-color-primary) !important}.sd-bg-text-primary{color:var(--sd-color-primary-text) !important}button.sd-bg-primary:focus,button.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}a.sd-bg-primary:focus,a.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}.sd-bg-secondary{background-color:var(--sd-color-secondary) !important}.sd-bg-text-secondary{color:var(--sd-color-secondary-text) !important}button.sd-bg-secondary:focus,button.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}a.sd-bg-secondary:focus,a.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}.sd-bg-success{background-color:var(--sd-color-success) !important}.sd-bg-text-success{color:var(--sd-color-success-text) !important}button.sd-bg-success:focus,button.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}a.sd-bg-success:focus,a.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}.sd-bg-info{background-color:var(--sd-color-info) !important}.sd-bg-text-info{color:var(--sd-color-info-text) !important}button.sd-bg-info:focus,button.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}a.sd-bg-info:focus,a.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}.sd-bg-warning{background-color:var(--sd-color-warning) !important}.sd-bg-text-warning{color:var(--sd-color-warning-text) !important}button.sd-bg-warning:focus,button.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}a.sd-bg-warning:focus,a.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}.sd-bg-danger{background-color:var(--sd-color-danger) !important}.sd-bg-text-danger{color:var(--sd-color-danger-text) !important}button.sd-bg-danger:focus,button.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}a.sd-bg-danger:focus,a.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}.sd-bg-light{background-color:var(--sd-color-light) !important}.sd-bg-text-light{color:var(--sd-color-light-text) !important}button.sd-bg-light:focus,button.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}a.sd-bg-light:focus,a.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}.sd-bg-muted{background-color:var(--sd-color-muted) !important}.sd-bg-text-muted{color:var(--sd-color-muted-text) !important}button.sd-bg-muted:focus,button.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}a.sd-bg-muted:focus,a.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}.sd-bg-dark{background-color:var(--sd-color-dark) !important}.sd-bg-text-dark{color:var(--sd-color-dark-text) !important}button.sd-bg-dark:focus,button.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}a.sd-bg-dark:focus,a.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}.sd-bg-black{background-color:var(--sd-color-black) !important}.sd-bg-text-black{color:var(--sd-color-black-text) !important}button.sd-bg-black:focus,button.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}a.sd-bg-black:focus,a.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}.sd-bg-white{background-color:var(--sd-color-white) !important}.sd-bg-text-white{color:var(--sd-color-white-text) !important}button.sd-bg-white:focus,button.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}a.sd-bg-white:focus,a.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}.sd-text-primary,.sd-text-primary>p{color:var(--sd-color-primary) !important}a.sd-text-primary:focus,a.sd-text-primary:hover{color:var(--sd-color-primary-highlight) !important}.sd-text-secondary,.sd-text-secondary>p{color:var(--sd-color-secondary) !important}a.sd-text-secondary:focus,a.sd-text-secondary:hover{color:var(--sd-color-secondary-highlight) !important}.sd-text-success,.sd-text-success>p{color:var(--sd-color-success) !important}a.sd-text-success:focus,a.sd-text-success:hover{color:var(--sd-color-success-highlight) !important}.sd-text-info,.sd-text-info>p{color:var(--sd-color-info) !important}a.sd-text-info:focus,a.sd-text-info:hover{color:var(--sd-color-info-highlight) !important}.sd-text-warning,.sd-text-warning>p{color:var(--sd-color-warning) !important}a.sd-text-warning:focus,a.sd-text-warning:hover{color:var(--sd-color-warning-highlight) !important}.sd-text-danger,.sd-text-danger>p{color:var(--sd-color-danger) !important}a.sd-text-danger:focus,a.sd-text-danger:hover{color:var(--sd-color-danger-highlight) !important}.sd-text-light,.sd-text-light>p{color:var(--sd-color-light) !important}a.sd-text-light:focus,a.sd-text-light:hover{color:var(--sd-color-light-highlight) !important}.sd-text-muted,.sd-text-muted>p{color:var(--sd-color-muted) !important}a.sd-text-muted:focus,a.sd-text-muted:hover{color:var(--sd-color-muted-highlight) !important}.sd-text-dark,.sd-text-dark>p{color:var(--sd-color-dark) !important}a.sd-text-dark:focus,a.sd-text-dark:hover{color:var(--sd-color-dark-highlight) !important}.sd-text-black,.sd-text-black>p{color:var(--sd-color-black) !important}a.sd-text-black:focus,a.sd-text-black:hover{color:var(--sd-color-black-highlight) !important}.sd-text-white,.sd-text-white>p{color:var(--sd-color-white) !important}a.sd-text-white:focus,a.sd-text-white:hover{color:var(--sd-color-white-highlight) !important}.sd-outline-primary{border-color:var(--sd-color-primary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-primary:focus,a.sd-outline-primary:hover{border-color:var(--sd-color-primary-highlight) !important}.sd-outline-secondary{border-color:var(--sd-color-secondary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-secondary:focus,a.sd-outline-secondary:hover{border-color:var(--sd-color-secondary-highlight) !important}.sd-outline-success{border-color:var(--sd-color-success) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-success:focus,a.sd-outline-success:hover{border-color:var(--sd-color-success-highlight) !important}.sd-outline-info{border-color:var(--sd-color-info) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-info:focus,a.sd-outline-info:hover{border-color:var(--sd-color-info-highlight) !important}.sd-outline-warning{border-color:var(--sd-color-warning) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-warning:focus,a.sd-outline-warning:hover{border-color:var(--sd-color-warning-highlight) !important}.sd-outline-danger{border-color:var(--sd-color-danger) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-danger:focus,a.sd-outline-danger:hover{border-color:var(--sd-color-danger-highlight) !important}.sd-outline-light{border-color:var(--sd-color-light) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-light:focus,a.sd-outline-light:hover{border-color:var(--sd-color-light-highlight) !important}.sd-outline-muted{border-color:var(--sd-color-muted) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-muted:focus,a.sd-outline-muted:hover{border-color:var(--sd-color-muted-highlight) !important}.sd-outline-dark{border-color:var(--sd-color-dark) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-dark:focus,a.sd-outline-dark:hover{border-color:var(--sd-color-dark-highlight) !important}.sd-outline-black{border-color:var(--sd-color-black) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-black:focus,a.sd-outline-black:hover{border-color:var(--sd-color-black-highlight) !important}.sd-outline-white{border-color:var(--sd-color-white) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-white:focus,a.sd-outline-white:hover{border-color:var(--sd-color-white-highlight) !important}.sd-bg-transparent{background-color:transparent !important}.sd-outline-transparent{border-color:transparent !important}.sd-text-transparent{color:transparent !important}.sd-p-0{padding:0 !important}.sd-pt-0,.sd-py-0{padding-top:0 !important}.sd-pr-0,.sd-px-0{padding-right:0 !important}.sd-pb-0,.sd-py-0{padding-bottom:0 !important}.sd-pl-0,.sd-px-0{padding-left:0 !important}.sd-p-1{padding:.25rem !important}.sd-pt-1,.sd-py-1{padding-top:.25rem !important}.sd-pr-1,.sd-px-1{padding-right:.25rem !important}.sd-pb-1,.sd-py-1{padding-bottom:.25rem !important}.sd-pl-1,.sd-px-1{padding-left:.25rem !important}.sd-p-2{padding:.5rem !important}.sd-pt-2,.sd-py-2{padding-top:.5rem !important}.sd-pr-2,.sd-px-2{padding-right:.5rem !important}.sd-pb-2,.sd-py-2{padding-bottom:.5rem !important}.sd-pl-2,.sd-px-2{padding-left:.5rem !important}.sd-p-3{padding:1rem !important}.sd-pt-3,.sd-py-3{padding-top:1rem !important}.sd-pr-3,.sd-px-3{padding-right:1rem !important}.sd-pb-3,.sd-py-3{padding-bottom:1rem !important}.sd-pl-3,.sd-px-3{padding-left:1rem !important}.sd-p-4{padding:1.5rem !important}.sd-pt-4,.sd-py-4{padding-top:1.5rem !important}.sd-pr-4,.sd-px-4{padding-right:1.5rem !important}.sd-pb-4,.sd-py-4{padding-bottom:1.5rem !important}.sd-pl-4,.sd-px-4{padding-left:1.5rem !important}.sd-p-5{padding:3rem !important}.sd-pt-5,.sd-py-5{padding-top:3rem !important}.sd-pr-5,.sd-px-5{padding-right:3rem !important}.sd-pb-5,.sd-py-5{padding-bottom:3rem !important}.sd-pl-5,.sd-px-5{padding-left:3rem !important}.sd-m-auto{margin:auto !important}.sd-mt-auto,.sd-my-auto{margin-top:auto !important}.sd-mr-auto,.sd-mx-auto{margin-right:auto !important}.sd-mb-auto,.sd-my-auto{margin-bottom:auto !important}.sd-ml-auto,.sd-mx-auto{margin-left:auto !important}.sd-m-0{margin:0 !important}.sd-mt-0,.sd-my-0{margin-top:0 !important}.sd-mr-0,.sd-mx-0{margin-right:0 !important}.sd-mb-0,.sd-my-0{margin-bottom:0 !important}.sd-ml-0,.sd-mx-0{margin-left:0 !important}.sd-m-1{margin:.25rem !important}.sd-mt-1,.sd-my-1{margin-top:.25rem !important}.sd-mr-1,.sd-mx-1{margin-right:.25rem !important}.sd-mb-1,.sd-my-1{margin-bottom:.25rem !important}.sd-ml-1,.sd-mx-1{margin-left:.25rem !important}.sd-m-2{margin:.5rem !important}.sd-mt-2,.sd-my-2{margin-top:.5rem !important}.sd-mr-2,.sd-mx-2{margin-right:.5rem !important}.sd-mb-2,.sd-my-2{margin-bottom:.5rem !important}.sd-ml-2,.sd-mx-2{margin-left:.5rem !important}.sd-m-3{margin:1rem !important}.sd-mt-3,.sd-my-3{margin-top:1rem !important}.sd-mr-3,.sd-mx-3{margin-right:1rem !important}.sd-mb-3,.sd-my-3{margin-bottom:1rem !important}.sd-ml-3,.sd-mx-3{margin-left:1rem !important}.sd-m-4{margin:1.5rem !important}.sd-mt-4,.sd-my-4{margin-top:1.5rem !important}.sd-mr-4,.sd-mx-4{margin-right:1.5rem !important}.sd-mb-4,.sd-my-4{margin-bottom:1.5rem !important}.sd-ml-4,.sd-mx-4{margin-left:1.5rem !important}.sd-m-5{margin:3rem !important}.sd-mt-5,.sd-my-5{margin-top:3rem !important}.sd-mr-5,.sd-mx-5{margin-right:3rem !important}.sd-mb-5,.sd-my-5{margin-bottom:3rem !important}.sd-ml-5,.sd-mx-5{margin-left:3rem !important}.sd-w-25{width:25% !important}.sd-w-50{width:50% !important}.sd-w-75{width:75% !important}.sd-w-100{width:100% !important}.sd-w-auto{width:auto !important}.sd-h-25{height:25% !important}.sd-h-50{height:50% !important}.sd-h-75{height:75% !important}.sd-h-100{height:100% !important}.sd-h-auto{height:auto !important}.sd-d-none{display:none !important}.sd-d-inline{display:inline !important}.sd-d-inline-block{display:inline-block !important}.sd-d-block{display:block !important}.sd-d-grid{display:grid !important}.sd-d-flex-row{display:-ms-flexbox !important;display:flex !important;flex-direction:row !important}.sd-d-flex-column{display:-ms-flexbox !important;display:flex !important;flex-direction:column !important}.sd-d-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}@media(min-width: 576px){.sd-d-sm-none{display:none !important}.sd-d-sm-inline{display:inline !important}.sd-d-sm-inline-block{display:inline-block !important}.sd-d-sm-block{display:block !important}.sd-d-sm-grid{display:grid !important}.sd-d-sm-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-sm-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 768px){.sd-d-md-none{display:none !important}.sd-d-md-inline{display:inline !important}.sd-d-md-inline-block{display:inline-block !important}.sd-d-md-block{display:block !important}.sd-d-md-grid{display:grid !important}.sd-d-md-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-md-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 992px){.sd-d-lg-none{display:none !important}.sd-d-lg-inline{display:inline !important}.sd-d-lg-inline-block{display:inline-block !important}.sd-d-lg-block{display:block !important}.sd-d-lg-grid{display:grid !important}.sd-d-lg-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-lg-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 1200px){.sd-d-xl-none{display:none !important}.sd-d-xl-inline{display:inline !important}.sd-d-xl-inline-block{display:inline-block !important}.sd-d-xl-block{display:block !important}.sd-d-xl-grid{display:grid !important}.sd-d-xl-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-xl-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}.sd-align-major-start{justify-content:flex-start !important}.sd-align-major-end{justify-content:flex-end !important}.sd-align-major-center{justify-content:center !important}.sd-align-major-justify{justify-content:space-between !important}.sd-align-major-spaced{justify-content:space-evenly !important}.sd-align-minor-start{align-items:flex-start !important}.sd-align-minor-end{align-items:flex-end !important}.sd-align-minor-center{align-items:center !important}.sd-align-minor-stretch{align-items:stretch !important}.sd-text-justify{text-align:justify !important}.sd-text-left{text-align:left !important}.sd-text-right{text-align:right !important}.sd-text-center{text-align:center !important}.sd-font-weight-light{font-weight:300 !important}.sd-font-weight-lighter{font-weight:lighter !important}.sd-font-weight-normal{font-weight:400 !important}.sd-font-weight-bold{font-weight:700 !important}.sd-font-weight-bolder{font-weight:bolder !important}.sd-font-italic{font-style:italic !important}.sd-text-decoration-none{text-decoration:none !important}.sd-text-lowercase{text-transform:lowercase !important}.sd-text-uppercase{text-transform:uppercase !important}.sd-text-capitalize{text-transform:capitalize !important}.sd-text-wrap{white-space:normal !important}.sd-text-nowrap{white-space:nowrap !important}.sd-text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-fs-1,.sd-fs-1>p{font-size:calc(1.375rem + 1.5vw) !important;line-height:unset !important}.sd-fs-2,.sd-fs-2>p{font-size:calc(1.325rem + 0.9vw) !important;line-height:unset !important}.sd-fs-3,.sd-fs-3>p{font-size:calc(1.3rem + 0.6vw) !important;line-height:unset !important}.sd-fs-4,.sd-fs-4>p{font-size:calc(1.275rem + 0.3vw) !important;line-height:unset !important}.sd-fs-5,.sd-fs-5>p{font-size:1.25rem !important;line-height:unset !important}.sd-fs-6,.sd-fs-6>p{font-size:1rem !important;line-height:unset !important}.sd-border-0{border:0 solid !important}.sd-border-top-0{border-top:0 solid !important}.sd-border-bottom-0{border-bottom:0 solid !important}.sd-border-right-0{border-right:0 solid !important}.sd-border-left-0{border-left:0 solid !important}.sd-border-1{border:1px solid !important}.sd-border-top-1{border-top:1px solid !important}.sd-border-bottom-1{border-bottom:1px solid !important}.sd-border-right-1{border-right:1px solid !important}.sd-border-left-1{border-left:1px solid !important}.sd-border-2{border:2px solid !important}.sd-border-top-2{border-top:2px solid !important}.sd-border-bottom-2{border-bottom:2px solid !important}.sd-border-right-2{border-right:2px solid !important}.sd-border-left-2{border-left:2px solid !important}.sd-border-3{border:3px solid !important}.sd-border-top-3{border-top:3px solid !important}.sd-border-bottom-3{border-bottom:3px solid !important}.sd-border-right-3{border-right:3px solid !important}.sd-border-left-3{border-left:3px solid !important}.sd-border-4{border:4px solid !important}.sd-border-top-4{border-top:4px solid !important}.sd-border-bottom-4{border-bottom:4px solid !important}.sd-border-right-4{border-right:4px solid !important}.sd-border-left-4{border-left:4px solid !important}.sd-border-5{border:5px solid !important}.sd-border-top-5{border-top:5px solid !important}.sd-border-bottom-5{border-bottom:5px solid !important}.sd-border-right-5{border-right:5px solid !important}.sd-border-left-5{border-left:5px solid !important}.sd-rounded-0{border-radius:0 !important}.sd-rounded-1{border-radius:.2rem !important}.sd-rounded-2{border-radius:.3rem !important}.sd-rounded-3{border-radius:.5rem !important}.sd-rounded-pill{border-radius:50rem !important}.sd-rounded-circle{border-radius:50% !important}.shadow-none{box-shadow:none !important}.sd-shadow-sm{box-shadow:0 .125rem .25rem var(--sd-color-shadow) !important}.sd-shadow-md{box-shadow:0 .5rem 1rem var(--sd-color-shadow) !important}.sd-shadow-lg{box-shadow:0 1rem 3rem var(--sd-color-shadow) !important}@keyframes sd-slide-from-left{0%{transform:translateX(-100%)}100%{transform:translateX(0)}}@keyframes sd-slide-from-right{0%{transform:translateX(200%)}100%{transform:translateX(0)}}@keyframes sd-grow100{0%{transform:scale(0);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50{0%{transform:scale(0.5);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50-rot20{0%{transform:scale(0.5) rotateZ(-20deg);opacity:.5}75%{transform:scale(1) rotateZ(5deg);opacity:1}95%{transform:scale(1) rotateZ(-1deg);opacity:1}100%{transform:scale(1) rotateZ(0);opacity:1}}.sd-animate-slide-from-left{animation:1s ease-out 0s 1 normal none running sd-slide-from-left}.sd-animate-slide-from-right{animation:1s ease-out 0s 1 normal none running sd-slide-from-right}.sd-animate-grow100{animation:1s ease-out 0s 1 normal none running sd-grow100}.sd-animate-grow50{animation:1s ease-out 0s 1 normal none running sd-grow50}.sd-animate-grow50-rot20{animation:1s ease-out 0s 1 normal none running sd-grow50-rot20}.sd-badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.sd-badge:empty{display:none}a.sd-badge{text-decoration:none}.sd-btn .sd-badge{position:relative;top:-1px}.sd-btn{background-color:transparent;border:1px solid transparent;border-radius:.25rem;cursor:pointer;display:inline-block;font-weight:400;font-size:1rem;line-height:1.5;padding:.375rem .75rem;text-align:center;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;vertical-align:middle;user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none}.sd-btn:hover{text-decoration:none}@media(prefers-reduced-motion: reduce){.sd-btn{transition:none}}.sd-btn-primary,.sd-btn-outline-primary:hover,.sd-btn-outline-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-primary:hover,.sd-btn-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary-highlight) !important;border-color:var(--sd-color-primary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-primary{color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary,.sd-btn-outline-secondary:hover,.sd-btn-outline-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary:hover,.sd-btn-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary-highlight) !important;border-color:var(--sd-color-secondary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-secondary{color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success,.sd-btn-outline-success:hover,.sd-btn-outline-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success:hover,.sd-btn-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success-highlight) !important;border-color:var(--sd-color-success-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-success{color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info,.sd-btn-outline-info:hover,.sd-btn-outline-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info:hover,.sd-btn-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info-highlight) !important;border-color:var(--sd-color-info-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-info{color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning,.sd-btn-outline-warning:hover,.sd-btn-outline-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning:hover,.sd-btn-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning-highlight) !important;border-color:var(--sd-color-warning-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-warning{color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger,.sd-btn-outline-danger:hover,.sd-btn-outline-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger:hover,.sd-btn-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger-highlight) !important;border-color:var(--sd-color-danger-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-danger{color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light,.sd-btn-outline-light:hover,.sd-btn-outline-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light:hover,.sd-btn-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light-highlight) !important;border-color:var(--sd-color-light-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-light{color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted,.sd-btn-outline-muted:hover,.sd-btn-outline-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted:hover,.sd-btn-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted-highlight) !important;border-color:var(--sd-color-muted-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-muted{color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark,.sd-btn-outline-dark:hover,.sd-btn-outline-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark:hover,.sd-btn-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark-highlight) !important;border-color:var(--sd-color-dark-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-dark{color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black,.sd-btn-outline-black:hover,.sd-btn-outline-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black:hover,.sd-btn-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black-highlight) !important;border-color:var(--sd-color-black-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-black{color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white,.sd-btn-outline-white:hover,.sd-btn-outline-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white:hover,.sd-btn-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white-highlight) !important;border-color:var(--sd-color-white-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-white{color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.sd-hide-link-text{font-size:0}.sd-octicon,.sd-material-icon{display:inline-block;fill:currentColor;vertical-align:middle}.sd-avatar-xs{border-radius:50%;object-fit:cover;object-position:center;width:1rem;height:1rem}.sd-avatar-sm{border-radius:50%;object-fit:cover;object-position:center;width:3rem;height:3rem}.sd-avatar-md{border-radius:50%;object-fit:cover;object-position:center;width:5rem;height:5rem}.sd-avatar-lg{border-radius:50%;object-fit:cover;object-position:center;width:7rem;height:7rem}.sd-avatar-xl{border-radius:50%;object-fit:cover;object-position:center;width:10rem;height:10rem}.sd-avatar-inherit{border-radius:50%;object-fit:cover;object-position:center;width:inherit;height:inherit}.sd-avatar-initial{border-radius:50%;object-fit:cover;object-position:center;width:initial;height:initial}.sd-card{background-clip:border-box;background-color:var(--sd-color-card-background);border:1px solid var(--sd-color-card-border);border-radius:.25rem;color:var(--sd-color-card-text);display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative;word-wrap:break-word}.sd-card>hr{margin-left:0;margin-right:0}.sd-card-hover:hover{border-color:var(--sd-color-card-border-hover);transform:scale(1.01)}.sd-card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem 1rem}.sd-card-title{margin-bottom:.5rem}.sd-card-subtitle{margin-top:-0.25rem;margin-bottom:0}.sd-card-text:last-child{margin-bottom:0}.sd-card-link:hover{text-decoration:none}.sd-card-link+.card-link{margin-left:1rem}.sd-card-header{padding:.5rem 1rem;margin-bottom:0;background-color:var(--sd-color-card-header);border-bottom:1px solid var(--sd-color-card-border)}.sd-card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.sd-card-footer{padding:.5rem 1rem;background-color:var(--sd-color-card-footer);border-top:1px solid var(--sd-color-card-border)}.sd-card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.sd-card-header-tabs{margin-right:-0.5rem;margin-bottom:-0.5rem;margin-left:-0.5rem;border-bottom:0}.sd-card-header-pills{margin-right:-0.5rem;margin-left:-0.5rem}.sd-card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom,.sd-card-img-top{width:100%}.sd-card-img,.sd-card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom{border-bottom-left-radius:calc(0.25rem - 1px);border-bottom-right-radius:calc(0.25rem - 1px)}.sd-cards-carousel{width:100%;display:flex;flex-wrap:nowrap;-ms-flex-direction:row;flex-direction:row;overflow-x:hidden;scroll-snap-type:x mandatory}.sd-cards-carousel.sd-show-scrollbar{overflow-x:auto}.sd-cards-carousel:hover,.sd-cards-carousel:focus{overflow-x:auto}.sd-cards-carousel>.sd-card{flex-shrink:0;scroll-snap-align:start}.sd-cards-carousel>.sd-card:not(:last-child){margin-right:3px}.sd-card-cols-1>.sd-card{width:90%}.sd-card-cols-2>.sd-card{width:45%}.sd-card-cols-3>.sd-card{width:30%}.sd-card-cols-4>.sd-card{width:22.5%}.sd-card-cols-5>.sd-card{width:18%}.sd-card-cols-6>.sd-card{width:15%}.sd-card-cols-7>.sd-card{width:12.8571428571%}.sd-card-cols-8>.sd-card{width:11.25%}.sd-card-cols-9>.sd-card{width:10%}.sd-card-cols-10>.sd-card{width:9%}.sd-card-cols-11>.sd-card{width:8.1818181818%}.sd-card-cols-12>.sd-card{width:7.5%}.sd-container,.sd-container-fluid,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container-xl{margin-left:auto;margin-right:auto;padding-left:var(--sd-gutter-x, 0.75rem);padding-right:var(--sd-gutter-x, 0.75rem);width:100%}@media(min-width: 576px){.sd-container-sm,.sd-container{max-width:540px}}@media(min-width: 768px){.sd-container-md,.sd-container-sm,.sd-container{max-width:720px}}@media(min-width: 992px){.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:960px}}@media(min-width: 1200px){.sd-container-xl,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:1140px}}.sd-row{--sd-gutter-x: 1.5rem;--sd-gutter-y: 0;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-top:calc(var(--sd-gutter-y) * -1);margin-right:calc(var(--sd-gutter-x) * -0.5);margin-left:calc(var(--sd-gutter-x) * -0.5)}.sd-row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--sd-gutter-x) * 0.5);padding-left:calc(var(--sd-gutter-x) * 0.5);margin-top:var(--sd-gutter-y)}.sd-col{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-auto>*{flex:0 0 auto;width:auto}.sd-row-cols-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}@media(min-width: 576px){.sd-col-sm{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-sm-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-sm-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-sm-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-sm-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-sm-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-sm-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-sm-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-sm-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-sm-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-sm-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-sm-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-sm-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-sm-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 768px){.sd-col-md{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-md-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-md-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-md-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-md-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-md-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-md-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-md-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-md-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-md-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-md-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-md-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-md-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-md-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 992px){.sd-col-lg{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-lg-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-lg-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-lg-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-lg-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-lg-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-lg-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-lg-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-lg-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-lg-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-lg-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-lg-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-lg-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-lg-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 1200px){.sd-col-xl{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-xl-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-xl-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-xl-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-xl-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-xl-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-xl-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-xl-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-xl-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-xl-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-xl-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-xl-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-xl-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-xl-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}.sd-col-auto{flex:0 0 auto;-ms-flex:0 0 auto;width:auto}.sd-col-1{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}.sd-col-2{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-col-3{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-col-4{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-col-5{flex:0 0 auto;-ms-flex:0 0 auto;width:41.6666666667%}.sd-col-6{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-col-7{flex:0 0 auto;-ms-flex:0 0 auto;width:58.3333333333%}.sd-col-8{flex:0 0 auto;-ms-flex:0 0 auto;width:66.6666666667%}.sd-col-9{flex:0 0 auto;-ms-flex:0 0 auto;width:75%}.sd-col-10{flex:0 0 auto;-ms-flex:0 0 auto;width:83.3333333333%}.sd-col-11{flex:0 0 auto;-ms-flex:0 0 auto;width:91.6666666667%}.sd-col-12{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-g-0,.sd-gy-0{--sd-gutter-y: 0}.sd-g-0,.sd-gx-0{--sd-gutter-x: 0}.sd-g-1,.sd-gy-1{--sd-gutter-y: 0.25rem}.sd-g-1,.sd-gx-1{--sd-gutter-x: 0.25rem}.sd-g-2,.sd-gy-2{--sd-gutter-y: 0.5rem}.sd-g-2,.sd-gx-2{--sd-gutter-x: 0.5rem}.sd-g-3,.sd-gy-3{--sd-gutter-y: 1rem}.sd-g-3,.sd-gx-3{--sd-gutter-x: 1rem}.sd-g-4,.sd-gy-4{--sd-gutter-y: 1.5rem}.sd-g-4,.sd-gx-4{--sd-gutter-x: 1.5rem}.sd-g-5,.sd-gy-5{--sd-gutter-y: 3rem}.sd-g-5,.sd-gx-5{--sd-gutter-x: 3rem}@media(min-width: 576px){.sd-col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-sm-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-sm-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-sm-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-sm-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-sm-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-sm-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-sm-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-sm-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-sm-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-sm-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-sm-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-sm-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-sm-0,.sd-gy-sm-0{--sd-gutter-y: 0}.sd-g-sm-0,.sd-gx-sm-0{--sd-gutter-x: 0}.sd-g-sm-1,.sd-gy-sm-1{--sd-gutter-y: 0.25rem}.sd-g-sm-1,.sd-gx-sm-1{--sd-gutter-x: 0.25rem}.sd-g-sm-2,.sd-gy-sm-2{--sd-gutter-y: 0.5rem}.sd-g-sm-2,.sd-gx-sm-2{--sd-gutter-x: 0.5rem}.sd-g-sm-3,.sd-gy-sm-3{--sd-gutter-y: 1rem}.sd-g-sm-3,.sd-gx-sm-3{--sd-gutter-x: 1rem}.sd-g-sm-4,.sd-gy-sm-4{--sd-gutter-y: 1.5rem}.sd-g-sm-4,.sd-gx-sm-4{--sd-gutter-x: 1.5rem}.sd-g-sm-5,.sd-gy-sm-5{--sd-gutter-y: 3rem}.sd-g-sm-5,.sd-gx-sm-5{--sd-gutter-x: 3rem}}@media(min-width: 768px){.sd-col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-md-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-md-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-md-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-md-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-md-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-md-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-md-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-md-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-md-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-md-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-md-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-md-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-md-0,.sd-gy-md-0{--sd-gutter-y: 0}.sd-g-md-0,.sd-gx-md-0{--sd-gutter-x: 0}.sd-g-md-1,.sd-gy-md-1{--sd-gutter-y: 0.25rem}.sd-g-md-1,.sd-gx-md-1{--sd-gutter-x: 0.25rem}.sd-g-md-2,.sd-gy-md-2{--sd-gutter-y: 0.5rem}.sd-g-md-2,.sd-gx-md-2{--sd-gutter-x: 0.5rem}.sd-g-md-3,.sd-gy-md-3{--sd-gutter-y: 1rem}.sd-g-md-3,.sd-gx-md-3{--sd-gutter-x: 1rem}.sd-g-md-4,.sd-gy-md-4{--sd-gutter-y: 1.5rem}.sd-g-md-4,.sd-gx-md-4{--sd-gutter-x: 1.5rem}.sd-g-md-5,.sd-gy-md-5{--sd-gutter-y: 3rem}.sd-g-md-5,.sd-gx-md-5{--sd-gutter-x: 3rem}}@media(min-width: 992px){.sd-col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-lg-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-lg-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-lg-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-lg-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-lg-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-lg-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-lg-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-lg-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-lg-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-lg-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-lg-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-lg-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-lg-0,.sd-gy-lg-0{--sd-gutter-y: 0}.sd-g-lg-0,.sd-gx-lg-0{--sd-gutter-x: 0}.sd-g-lg-1,.sd-gy-lg-1{--sd-gutter-y: 0.25rem}.sd-g-lg-1,.sd-gx-lg-1{--sd-gutter-x: 0.25rem}.sd-g-lg-2,.sd-gy-lg-2{--sd-gutter-y: 0.5rem}.sd-g-lg-2,.sd-gx-lg-2{--sd-gutter-x: 0.5rem}.sd-g-lg-3,.sd-gy-lg-3{--sd-gutter-y: 1rem}.sd-g-lg-3,.sd-gx-lg-3{--sd-gutter-x: 1rem}.sd-g-lg-4,.sd-gy-lg-4{--sd-gutter-y: 1.5rem}.sd-g-lg-4,.sd-gx-lg-4{--sd-gutter-x: 1.5rem}.sd-g-lg-5,.sd-gy-lg-5{--sd-gutter-y: 3rem}.sd-g-lg-5,.sd-gx-lg-5{--sd-gutter-x: 3rem}}@media(min-width: 1200px){.sd-col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-xl-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-xl-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-xl-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-xl-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-xl-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-xl-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-xl-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-xl-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-xl-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-xl-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-xl-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-xl-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-xl-0,.sd-gy-xl-0{--sd-gutter-y: 0}.sd-g-xl-0,.sd-gx-xl-0{--sd-gutter-x: 0}.sd-g-xl-1,.sd-gy-xl-1{--sd-gutter-y: 0.25rem}.sd-g-xl-1,.sd-gx-xl-1{--sd-gutter-x: 0.25rem}.sd-g-xl-2,.sd-gy-xl-2{--sd-gutter-y: 0.5rem}.sd-g-xl-2,.sd-gx-xl-2{--sd-gutter-x: 0.5rem}.sd-g-xl-3,.sd-gy-xl-3{--sd-gutter-y: 1rem}.sd-g-xl-3,.sd-gx-xl-3{--sd-gutter-x: 1rem}.sd-g-xl-4,.sd-gy-xl-4{--sd-gutter-y: 1.5rem}.sd-g-xl-4,.sd-gx-xl-4{--sd-gutter-x: 1.5rem}.sd-g-xl-5,.sd-gy-xl-5{--sd-gutter-y: 3rem}.sd-g-xl-5,.sd-gx-xl-5{--sd-gutter-x: 3rem}}.sd-flex-row-reverse{flex-direction:row-reverse !important}details.sd-dropdown{position:relative;font-size:var(--sd-fontsize-dropdown)}details.sd-dropdown:hover{cursor:pointer}details.sd-dropdown .sd-summary-content{cursor:default}details.sd-dropdown summary.sd-summary-title{padding:.5em 1em;font-size:var(--sd-fontsize-dropdown-title);font-weight:var(--sd-fontweight-dropdown-title);user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;list-style:none;display:inline-flex;justify-content:space-between}details.sd-dropdown summary.sd-summary-title::-webkit-details-marker{display:none}details.sd-dropdown summary.sd-summary-title:focus{outline:none}details.sd-dropdown summary.sd-summary-title .sd-summary-icon{margin-right:.6em;display:inline-flex;align-items:center}details.sd-dropdown summary.sd-summary-title .sd-summary-icon svg{opacity:.8}details.sd-dropdown summary.sd-summary-title .sd-summary-text{flex-grow:1;line-height:1.5;padding-right:.5rem}details.sd-dropdown summary.sd-summary-title .sd-summary-state-marker{pointer-events:none;display:inline-flex;align-items:center}details.sd-dropdown summary.sd-summary-title .sd-summary-state-marker svg{opacity:.6}details.sd-dropdown summary.sd-summary-title:hover .sd-summary-state-marker svg{opacity:1;transform:scale(1.1)}details.sd-dropdown[open] summary .sd-octicon.no-title{visibility:hidden}details.sd-dropdown .sd-summary-chevron-right{transition:.25s}details.sd-dropdown[open]>.sd-summary-title .sd-summary-chevron-right{transform:rotate(90deg)}details.sd-dropdown[open]>.sd-summary-title .sd-summary-chevron-down{transform:rotate(180deg)}details.sd-dropdown:not([open]).sd-card{border:none}details.sd-dropdown:not([open])>.sd-card-header{border:1px solid var(--sd-color-card-border);border-radius:.25rem}details.sd-dropdown.sd-fade-in[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out;animation:sd-fade-in .5s ease-in-out}details.sd-dropdown.sd-fade-in-slide-down[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out}.sd-col>.sd-dropdown{width:100%}.sd-summary-content>.sd-tab-set:first-child{margin-top:0}@keyframes sd-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes sd-slide-down{0%{transform:translate(0, -10px)}100%{transform:translate(0, 0)}}.sd-tab-set{border-radius:.125rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.sd-tab-set>input{opacity:0;position:absolute}.sd-tab-set>input:checked+label{border-color:var(--sd-color-tabs-underline-active);color:var(--sd-color-tabs-label-active)}.sd-tab-set>input:checked+label+.sd-tab-content{display:block}.sd-tab-set>input:not(:checked)+label:hover{color:var(--sd-color-tabs-label-hover);border-color:var(--sd-color-tabs-underline-hover)}.sd-tab-set>input:focus+label{outline-style:auto}.sd-tab-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.sd-tab-set>label{border-bottom:.125rem solid transparent;margin-bottom:0;color:var(--sd-color-tabs-label-inactive);border-color:var(--sd-color-tabs-underline-inactive);cursor:pointer;font-size:var(--sd-fontsize-tabs-label);font-weight:700;padding:1em 1.25em .5em;transition:color 250ms;width:auto;z-index:1}html .sd-tab-set>label:hover{color:var(--sd-color-tabs-label-active)}.sd-col>.sd-tab-set{width:100%}.sd-tab-content{box-shadow:0 -0.0625rem var(--sd-color-tabs-overline),0 .0625rem var(--sd-color-tabs-underline);display:none;order:99;padding-bottom:.75rem;padding-top:.75rem;width:100%}.sd-tab-content>:first-child{margin-top:0 !important}.sd-tab-content>:last-child{margin-bottom:0 !important}.sd-tab-content>.sd-tab-set{margin:0}.sd-sphinx-override,.sd-sphinx-override *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.sd-sphinx-override p{margin-top:0}:root{--sd-color-primary: #0071bc;--sd-color-secondary: #6c757d;--sd-color-success: #28a745;--sd-color-info: #17a2b8;--sd-color-warning: #f0b37e;--sd-color-danger: #dc3545;--sd-color-light: #f8f9fa;--sd-color-muted: #6c757d;--sd-color-dark: #212529;--sd-color-black: black;--sd-color-white: white;--sd-color-primary-highlight: #0060a0;--sd-color-secondary-highlight: #5c636a;--sd-color-success-highlight: #228e3b;--sd-color-info-highlight: #148a9c;--sd-color-warning-highlight: #cc986b;--sd-color-danger-highlight: #bb2d3b;--sd-color-light-highlight: #d3d4d5;--sd-color-muted-highlight: #5c636a;--sd-color-dark-highlight: #1c1f23;--sd-color-black-highlight: black;--sd-color-white-highlight: #d9d9d9;--sd-color-primary-bg: rgba(0, 113, 188, 0.2);--sd-color-secondary-bg: rgba(108, 117, 125, 0.2);--sd-color-success-bg: rgba(40, 167, 69, 0.2);--sd-color-info-bg: rgba(23, 162, 184, 0.2);--sd-color-warning-bg: rgba(240, 179, 126, 0.2);--sd-color-danger-bg: rgba(220, 53, 69, 0.2);--sd-color-light-bg: rgba(248, 249, 250, 0.2);--sd-color-muted-bg: rgba(108, 117, 125, 0.2);--sd-color-dark-bg: rgba(33, 37, 41, 0.2);--sd-color-black-bg: rgba(0, 0, 0, 0.2);--sd-color-white-bg: rgba(255, 255, 255, 0.2);--sd-color-primary-text: #fff;--sd-color-secondary-text: #fff;--sd-color-success-text: #fff;--sd-color-info-text: #fff;--sd-color-warning-text: #212529;--sd-color-danger-text: #fff;--sd-color-light-text: #212529;--sd-color-muted-text: #fff;--sd-color-dark-text: #fff;--sd-color-black-text: #fff;--sd-color-white-text: #212529;--sd-color-shadow: rgba(0, 0, 0, 0.15);--sd-color-card-border: rgba(0, 0, 0, 0.125);--sd-color-card-border-hover: hsla(231, 99%, 66%, 1);--sd-color-card-background: transparent;--sd-color-card-text: inherit;--sd-color-card-header: transparent;--sd-color-card-footer: transparent;--sd-color-tabs-label-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-hover: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-inactive: hsl(0, 0%, 66%);--sd-color-tabs-underline-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-underline-hover: rgba(178, 206, 245, 0.62);--sd-color-tabs-underline-inactive: transparent;--sd-color-tabs-overline: rgb(222, 222, 222);--sd-color-tabs-underline: rgb(222, 222, 222);--sd-fontsize-tabs-label: 1rem;--sd-fontsize-dropdown: inherit;--sd-fontsize-dropdown-title: 1rem;--sd-fontweight-dropdown-title: 700} diff --git a/version/0.1/_static/404.rst b/version/0.1/_static/404.rst new file mode 100644 index 000000000..c55991984 --- /dev/null +++ b/version/0.1/_static/404.rst @@ -0,0 +1,6 @@ +Oops! +===== + +This is unexpected. The page you are requesting does not exist. + +If this page should exist, please contact `{{ theme_contact_mail }} <{{ theme_contact_mail }}>`_. \ No newline at end of file diff --git a/version/0.1/_static/README.md b/version/0.1/_static/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/version/0.1/_static/_image/example004_thumbnail.png b/version/0.1/_static/_image/example004_thumbnail.png new file mode 100644 index 000000000..a6b1d8745 Binary files /dev/null and b/version/0.1/_static/_image/example004_thumbnail.png differ diff --git a/version/0.1/_static/ansys-favicon.png b/version/0.1/_static/ansys-favicon.png new file mode 100644 index 000000000..cfb2e5356 Binary files /dev/null and b/version/0.1/_static/ansys-favicon.png differ diff --git a/version/0.1/_static/ansys_logo_black.jpg b/version/0.1/_static/ansys_logo_black.jpg new file mode 100644 index 000000000..7f7373351 Binary files /dev/null and b/version/0.1/_static/ansys_logo_black.jpg differ diff --git a/version/0.1/_static/ansys_logo_black_cropped.jpg b/version/0.1/_static/ansys_logo_black_cropped.jpg new file mode 100644 index 000000000..05d98e6ca Binary files /dev/null and b/version/0.1/_static/ansys_logo_black_cropped.jpg differ diff --git a/version/0.1/_static/ansys_logo_white.pdf b/version/0.1/_static/ansys_logo_white.pdf new file mode 100644 index 000000000..fceb89b30 Binary files /dev/null and b/version/0.1/_static/ansys_logo_white.pdf differ diff --git a/version/0.1/_static/ansys_logo_white_cropped.pdf b/version/0.1/_static/ansys_logo_white_cropped.pdf new file mode 100644 index 000000000..ad122b159 Binary files /dev/null and b/version/0.1/_static/ansys_logo_white_cropped.pdf differ diff --git a/version/0.1/_static/basic.css b/version/0.1/_static/basic.css new file mode 100644 index 000000000..2af6139e6 --- /dev/null +++ b/version/0.1/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 270px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/version/0.1/_static/binder_badge_logo.svg b/version/0.1/_static/binder_badge_logo.svg new file mode 100644 index 000000000..327f6b639 --- /dev/null +++ b/version/0.1/_static/binder_badge_logo.svg @@ -0,0 +1 @@ + launchlaunchbinderbinder \ No newline at end of file diff --git a/version/0.1/_static/broken_example.png b/version/0.1/_static/broken_example.png new file mode 100644 index 000000000..4fea24e7d Binary files /dev/null and b/version/0.1/_static/broken_example.png differ diff --git a/version/0.1/_static/check-solid.svg b/version/0.1/_static/check-solid.svg new file mode 100644 index 000000000..92fad4b5c --- /dev/null +++ b/version/0.1/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/version/0.1/_static/clipboard.min.js b/version/0.1/_static/clipboard.min.js new file mode 100644 index 000000000..54b3c4638 --- /dev/null +++ b/version/0.1/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/version/0.1/_static/copybutton.css b/version/0.1/_static/copybutton.css new file mode 100644 index 000000000..f1916ec7d --- /dev/null +++ b/version/0.1/_static/copybutton.css @@ -0,0 +1,94 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +/* Show the copybutton */ +.highlight:hover button.copybtn, button.copybtn.success { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/version/0.1/_static/copybutton.js b/version/0.1/_static/copybutton.js new file mode 100644 index 000000000..2ea7ff3e2 --- /dev/null +++ b/version/0.1/_static/copybutton.js @@ -0,0 +1,248 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copier dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for a moment, then changes it back +// We want the timeout of our `success` class to be a bit shorter than the +// tooltip and icon change, so that we can hide the icon before changing back. +var timeoutIcon = 2000; +var timeoutSuccessClass = 1500; + +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + // Remove success a little bit sooner than we change the tooltip + // So that we can use CSS to hide the copybutton first + setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) + setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/version/0.1/_static/copybutton_funcs.js b/version/0.1/_static/copybutton_funcs.js new file mode 100644 index 000000000..dbe1aaad7 --- /dev/null +++ b/version/0.1/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/version/0.1/_static/css/ansys_sphinx_theme.css b/version/0.1/_static/css/ansys_sphinx_theme.css new file mode 100644 index 000000000..e0bc78237 --- /dev/null +++ b/version/0.1/_static/css/ansys_sphinx_theme.css @@ -0,0 +1,1138 @@ +/* Provided by the Sphinx base theme template at build time */ + +@import "../basic.css"; +@import url("https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&display=swap"); +@import "../sg_gallery.css"; +@import "../design-style.4045f2051d55cab465a707391d5b2007.min.css"; +@import "../styles/pydata-sphinx-theme.css"; + +@font-face { + font-family: "Source Sans Pro Light"; + src: url(../fonts/SourceSansPro-Light.ttf); +} + +@font-face { + font-family: "Source Sans Pro"; + src: url(../fonts/SourceSansPro-Regular.ttf); +} + +:root { + /* Ansys specific changes to the theme */ + + /***************************************************************************** + * Ansys Font family + **/ + /* These are adapted from https://systemfontstack.com/ */ + --pst-font-family-base-system: -apple-system, BlinkMacSystemFont, Segoe UI, + "Helvetica Neue", Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, + Segoe UI Symbol; + --pst-font-family-monospace-system: "SFMono-Regular", Menlo, Consolas, Monaco, + Liberation Mono, Lucida Console, monospace; + + --pst-font-family-base: "Source Sans Pro", sans-serif, + var(--pst-font-family-base-system); + --pst-font-family-heading: "Source Sans Pro", sans-serif, + var(--pst-font-family-base-system); + --pst-font-family-monospace: monospace, Courier, + var(--pst-font-family-monospace-system); + + /***************************************************************************** + * Ansys compatible colors + * + * Colors are defined in rgb string way, "red, green, blue" + **/ + --ansysGold: rgb(255, 183, 27); /* #FFB71B */ + --ansysBronze: rgb(200, 146, 17); /* #C89211 */ + --pythonBlue: rgb(57, 114, 161); /* #3972a1 */ + + --pst-color-active-navigation: var(--ansysBronze); /* --ansysBronze */ + --pst-color-navbar-link: rgb(255, 255, 255); + --pst-color-navbar-link-hover: var(--ansysBronze); /* --ansysBronze */ + --pst-color-navbar-link-active: var(--ansysGold); /* --ansysBronze */ + --pst-font-size-h1: 48px; + --pst-font-size-h2: 36px; + --pst-font-size-h3: 28px; + --pst-font-size-h4: 20px; + --pst-font-size-h5: 14px; + --pst-font-size-h6: 11px; +} + +html[data-theme="light"] { + /***************************************************************************** + * main colors + */ + --pst-color-primary: #fff; + --pst-color-secondary: rgb(200, 146, 17); + --pst-color-success: rgb(40, 167, 69); + --pst-color-text-base: rgb(0, 0, 0); + --pst-color-text-muted: rgb(26, 24, 24); + --pst-color-border: #a19d9d; + --pst-color-shadow: rgb(216, 216, 216); + --pst-color-info: var(--pst-color-link); + + /***************************************************************************** + * depth colors + */ + --pst-color-on-background: rgb(0, 0, 0); + --pst-color-on-surface: #f2f2f2; + + /***************************************************************************** + * extensions + */ + + --pst-color-panel-background: var(--pst-color-on-background); + + /***************************************************************************** + * layout + */ + + --pst-color-link: #1e6ddc; + --pst-color-link-hover: #32cfea; + --pst-color-link-visted: #9428f2; + --pst-color-inline-code: #000; + --pst-color-target: rgb(255, 255, 255); + + /***************************************************************************** + * color for sphinx-gallery-code output + */ + --pst-color-codecell: #fafae2; + --pst-color-codeout: var(--pst-color-inline-code); + --pst-color-sig: #0965c8; + --pst-color-code-s1: #b35000; + --pst-color-code-c1: #095d0a; + /***************************************************************************** + * sphinx design primary color + */ + --sd-color-primary: var(--pst-color-text-base); + + /***************************************************************************** + * table hovering + */ + --pst-color-table-hover: var(--pst-color-border); + + /***************************************************************************** + * search hide match + */ + --pst-color-search-match: #91969b; +} + +html[data-theme="dark"] { + /***************************************************************************** + * main colors + */ + --pst-color-primary: #d09735; + --pst-color-secondary: #c58e30; + --pst-color-success: rgb(72, 135, 87); + --pst-color-text-base: rgb(201, 209, 217); + --pst-color-text-muted: rgb(192, 192, 192); + --pst-color-border: rgb(192, 192, 192); + --pst-color-shadow: rgb(104, 102, 102); + --pst-color-background: rgb(18, 18, 18); + --pst-color-on-background: rgb(0, 0, 0); + --pst-color-surface: rgb(41, 41, 41); + --pst-color-on-surface: rgb(55, 55, 55); + --pst-color-info: var(--pst-color-secondary); + + /***************************************************************************** + * extensions + */ + + --pst-color-panel-background: var(--pst-color-on-background); + + /***************************************************************************** + * layout + */ + + --pst-color-link: #579ce5; + --pst-color-link-hover: #12b2e2; + --pst-color-link-visted: #c58af9; + --pst-color-inline-code: #fff; + --pst-color-target: rgb(71, 39, 0); + + /***************************************************************************** + * color for sphinx-gallery-code output + */ + --pst-color-codecell: #495057; + --pst-color-codeout: #f2f4f6; + --pst-color-sig: #d6ab1e; + --pst-color-code-s1: #d79a60; + --pst-color-code-c1: #8fb842; + + /***************************************************************************** + * table hovering + */ + --pst-color-table-hover: var(--pst-color-target); + + /***************************************************************************** + * search hide match + */ + --pst-color-search-match: var(--pst-color-primary); +} + +/* +################# +body and content +################# +*/ + +body { + font-family: "Open Sans", sans-serif; +} + +h1, +h2 { + color: var(--pst-color-text-base); +} + +/* +########## +Codecell +########## +*/ + +dt:target, +span.highlighted { + background-color: var(--pst-color-codecell) !important; +} + +.docutils { + color: var(--pst-color-inline-code); + font-family: var(--pst-font-family-monospace); + font-weight: 500; +} + +code.literal { + padding: 0.1rem 0.25rem; + padding-top: 0.1rem; + padding-right: 0.25rem; + padding-bottom: 0.1rem; + padding-left: 0.25rem; + background-color: var(--pst-color-on-surface); + border: 1px solid var(--pst-color-border); + border-radius: 0.25rem; +} + +.xref.std.std-ref { + color: var(--pst-color-inline-code); + font-family: "Inconsolata"; + font-weight: normal; + font-style: italic; + padding: 0.1rem 0.25rem; + padding-top: 0.1rem; + padding-right: 0.25rem; + padding-bottom: 0.1rem; + padding-left: 0.25rem; + font-size: 90%; + background-color: var(--pst-color-on-surface); + border: 1px solid var(--pst-color-border); + border-radius: 0.25rem; +} + +.sig { + font-family: "Consolas", "Menlo", "DejaVu Sans Mono", + "Bitstream Vera Sans Mono", monospace; +} + +.sig-name.descname { + color: var(--pst-color-inline-code); +} + +.sig-name { + color: var(--pst-color-sig); +} + +/* Increase empty-space around classes, methods, properties, etc. that are descendants + of other items */ +dl.class dl.py { + margin-top: 2.5em; + margin-bottom: 2.5em; +} + +/* Reduce empty-space around Notes and Examples headings */ +p.rubric { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + +/* +######## +Table +######## +*/ + +.table { + width: 100%; + max-width: 100%; + border-spacing: 0; + border-collapse: collapse; + overflow: hidden; + vertical-align: middle; + color: var(--pst-color-text-base); + /* Disabling scroll bars */ + overflow-y: scroll; + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* Internet Explorer 10+ */ +} + +tr { + background-color: var(--pst-color-background); +} + +th { + background-color: rgb(255, 183, 27, 0.55); +} + +tr:nth-child(odd), +tr:nth-child(even) { + background-color: var(--pst-color-background); +} + +.table tr:hover td { + background-color: var(--pst-color-table-hover); +} + +div.rendered_html table.dataframe td { + color: var(--pst-color-text-base); +} + +/* +############### +Table-centered +################ +Same as table but with horizontally centered text. + +see examples. +*/ +table.table-centered { + width: 100%; + max-width: 100%; + border-spacing: 0; + border-collapse: collapse; + overflow: hidden; + vertical-align: middle; + text-align: center; + + /* Disabling scroll bars */ + overflow-y: scroll; + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* Internet Explorer 10+ */ +} + +.table-centered tr { + background-color: var(--pst-color-background); + text-align: center; +} + +.table-centered th { + background-color: rgb(255, 183, 27, 0.55); + text-align: center; +} + +.table-centered tr:nth-child(odd), +.table-centered tr:nth-child(even) { + text-align: center; + background-color: var(--pst-color-background); +} + +.table-centered tr:hover td { + background-color: var(--pst-color-table-hover); + text-align: center; +} + +table.dataframe { + table-layout: auto !important; +} + +/* +################### +longtable-centered +#################### +*/ + +table.longtable-centered { + text-align: center; +} + +.longtable-centered tr { + text-align: center; +} + +.longtable-centered th { + text-align: center; +} + +.longtable-centered tr:nth-child(odd) { + text-align: center; +} + +.longtable-centered tr:nth-child(even) { + text-align: center; +} + +table.longtable-centered tr:hover td { + background-color: var(--pst-color-table-hover); + text-align: center; +} + +/* +######### +DataFrame +######### +*/ + +.dataframe tr { + background-color: var(--pst-color-background); +} + +.dataframe tr:nth-child(odd), +.dataframe tr:nth-child(even) { + background-color: var(--pst-color-background) !important; +} + +.dataframe tr:hover td { + background-color: var(--pst-color-table-hover); +} + +/* +################### +DataFrame-centered +################### +*/ + +.dataframe-centered tr { + background-color: var(--pst-color-background); + text-align: center; +} + +.dataframe-centered tr:nth-child(odd) { + background-color: var(--pst-color-background) !important; + text-align: center; +} + +.dataframe-centered tr:nth-child(even) { + background-color: var(--pst-color-border); + text-align: center; +} + +.dataframe-centered tr:hover td { + background-color: var(--pst-color-border); + text-align: center; +} + +.dataframe thead th { + text-align: center; +} + +/* +############ +data table +############ +*/ + +.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, +.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, +.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active { + color: var(--pst-color-text-base) !important; +} + +.dataTables_wrapper, +.dataTables_wrapper .dataTables_filter, +.dataTables_wrapper .dataTables_info, +.dataTables_wrapper .dataTables_processing, +.dataTables_wrapper .dataTables_paginate { + color: var(--pst-color-text-base) !important; +} + +table.dataTable tbody th, +table.dataTable tbody td { + background: var(--pst-color-background); +} + +label { + color: var(--pst-color-text-base); +} + +/* +########## +Scroll-bar +########## +*/ + +body::-webkit-scrollbar { + width: 1rem; + height: 1rem; +} + +body::-webkit-scrollbar-thumb { + background: var(--pst-color-border); + border-radius: inherit; +} + +body::-webkit-scrollbar-track { + background: var(--pst-color-background); +} + +.bd-sidebar::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; +} + +.bd-sidebar::-webkit-scrollbar-thumb { + background: var(--pst-color-border); + border-radius: inherit; + visibility: hidden !important; +} + +.bd-sidebar::-webkit-scrollbar-track { + background: var(--pst-color-border); + visibility: hidden !important; +} + +.bd-toc::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; +} + +.bd-toc::-webkit-scrollbar-thumb { + background: var(--pst-color-border); + border-radius: inherit; +} + +.bd-toc::-webkit-scrollbar-track { + background: var(--pst-color-border); +} + +/* +############ +Autosummary +############ +*/ + +.autosummary tr:nth-child(odd), +.autosummary tr:nth-child(even) { + background-color: var(--pst-color-background); +} + +/* +##################### +ReST :download: links +##################### +*/ +a > code.download { + font-family: var(--pst-font-family-base); + color: var(--pst-color-link); + text-decoration: none; + font-weight: normal; +} + +/* +############### +Dropdown button +############### +*/ + +.navbar button.navbar-toggler { + margin-right: 1em; + border-color: rgb(255, 255, 255); + color: rgb(255, 255, 255); +} + +/* +#################################################### +Side column size (first and second column from left) +#################################################### +*/ + +.col-md-3 { + flex: 0 0 20%; + max-width: 20%; +} + +a.headerlink { + color: #222; +} + +@media (min-width: 1200px) { + .container, + .container-lg, + .container-md, + .container-sm, + .container-xl { + max-width: 1200px; + } +} + +@media (min-width: 1600px) { + .container, + .container-lg, + .container-md, + .container-sm, + .container-xl { + max-width: 1600px; + } +} + +/* +############################################ +Navigation column (according to side column) +############################################ +*/ +.col-lg-9 { + padding-right: 5px; + padding-left: 20px; +} +.bd-main .bd-content { + display: flex; + height: 100%; + justify-content: end; +} + +.bd-main .bd-content .bd-article-container .bd-article { + padding-left: 1rem; + padding-top: 1rem; + padding-right: 1rem; +} +.bd-header .navbar-nav li a.nav-link { + color: #ddd; +} +.bd-header .navbar-nav .dropdown button { + color: #ddd; + display: unset; +} + +.theme-switch-button span { + color: #fff; +} + +/* +################################# +Syntax highlighting in code block +################################# +*/ + +html[data-theme="light"] .highlight .o { + color: #b35000; + font-weight: bold; +} + +/* +############################# +Bold font weight for **code** +############################# +*/ + +b, +strong { + font-weight: 900; +} + +.bd-header .navbar-header-items__start { + width: fit-content; + padding-right: 3rem; +} + +.navbar-nav li a:focus, +.navbar-nav li a:hover, +.navbar-nav li.current > a { + color: white !important; +} + +.navbar-nav .dropdown .dropdown-menu { + min-width: 250px; +} + +/* +########################### +Left side toc-tree hovering +########################### +*/ + +nav.bd-links .active:hover > a { + font-weight: bold; + color: var(--pst-color-text-base); + border-left: 2px solid var(--pst-color-text-base); +} + +nav.bd-links .active > a { + font-weight: bold; + color: var(--pst-color-text-muted) !important; + border-left: 2px solid var(--pst-color-text-base); + border-bottom: none !important; + padding-left: 0.5rem; +} + +nav.bd-links li > a:hover { + font-weight: 900; + color: var(--pst-color-link) !important; +} + +.bd-header .navbar-nav .dropdown button:hover { + color: white; +} + +.dropdown-item:hover { + font-weight: bold; + background-color: transparent; +} + +/* +################## +icon, button, logo +################## +*/ + +button.btn.version-switcher__button { + border-color: var(--pst-color-border); + color: #ddd; +} + +button.version-switcher__button { + color: #f8f9fa; +} + +.search-button { + color: #ddd; +} + +kbd { + background-color: #f8f9fa; +} + +/* +##################################### +icon, button, logo for small screen +##################################### +*/ +@media screen and (max-width: 576px) { + html[data-theme="light"] .theme-switch-button span { + color: rgb(19, 18, 18) !important; + } + button.btn.version-switcher__button { + border-color: var(--pst-color-border); + color: var(--pst-color-text-base); + font-weight: 700; + } + button.btn.version-switcher__button { + border-color: var(--pst-color-border); + color: var(--pst-color-text-base); + } + button.version-switcher__button { + color: var(--pst-color-text-base); + } + .navbar-nav li a:focus, + .navbar-nav li a:hover, + .navbar-nav li.current > a { + color: var(--pst-color-text-base) !important; + font-weight: 700; + } + button, + input, + optgroup, + select, + textarea, + button:hover { + color: var(--pst-color-text-base); + } +} + +.navbar-icon-links { + column-gap: 0.2rem; +} + +.search-button-field { + color: white; + background-color: transparent; +} + +.search-button-field:hover { + color: var(--pst-color-border); +} + +/* make the github logo white */ + +.version-switcher__menu a.list-group-item:hover { + background-color: #d09735 !important; + border-color: var(--pst-color-text-base) !important; + color: #000 !important; +} + +.version-switcher__menu a.list-group-item { + background-color: #f8f9fa; + border-color: var(--pst-color-border); + color: #000; +} + +.fa-wrench:before { + content: "\f0ad"; + color: white; +} + +html[data-theme="light"] #pst-back-to-top { + background-color: var(--pst-color-link); +} +/* +############################## +image padding before and after +############################## +*/ + +img { + padding-top: 1em; + padding-bottom: 1em; +} + +html[data-theme="dark"] .bd-content img:not(.only-dark):not(.dark-light) { + background: transparent; +} + +img.logo__image { + padding-top: 0rem; + padding-bottom: 0rem; +} + +/* +########################## +Nav-bar entity right side. +########################## +*/ + +nav.bd-links li > a { + color: var(--pst-color-link); + font-size: 0.98rem; +} + +html[data-theme="light"] .highlight pre { + line-height: 125%; + font-size: 0.9em; +} + +html[data-theme="dark"] .highlight pre { + line-height: 125%; + font-size: 0.9em; +} + +.bd-toc { + padding-top: 5em; +} + +#version_switcher_button { + background-color: var(--pst-color-background); +} + +.editthispage a { + color: var(--pst-color-text-base); +} + +.list-group-item.active { + z-index: 2; + color: var(--pst-color-text-base) !important; +} + +/* +############## +Sphinx design +############## +*/ + +blockquote { + background-color: var(--pst-color-background); +} +.sd-sphinx-override, +.sd-sphinx-override * { + flex: auto; +} + +/* Sphinx-design tab */ + +/* Common styles for all screen sizes */ +.sd-tab-set > input:not(.focus-visible) + label { + outline: none; + font-size: large; + -webkit-tap-highlight-color: var(--pst-color-background) !important; + color: var(--pst-color-text-base); +} + +.sd-tab-set > input:checked + label + .sd-tab-content { + display: block; + font-size: medium; +} + +.sd-tab-set > input:checked + label + .sd-tab-content { + display: block; + font-size: 0.9rem; +} + +.sd-tab-content { + font-family: var(--pst-font-family-base); +} + +.bd-content .sd-tab-set > input:checked + label, +.bd-content .sd-tab-set > input:not(:checked) + label:hover { + color: var(--pst-color-text-base); + border-color: var(--pst-color-text-base); +} + +/* Media query for medium-sized screens */ +@media screen and (max-width: 768px) { + .sd-tab-set > input:not(.focus-visible) + label { + font-size: medium; + } +} + +/* Media query for small-sized screens */ +@media screen and (max-width: 576px) { + .sd-tab-set > input:not(.focus-visible) + label { + font-size: small; + } +} + +/* Sphinx-design card */ + +/* Common styles for all screen sizes */ +.sd-card .sd-card-text { + font-family: var(--pst-font-family-base) !important; + background-color: var(--pst-color-background) !important; +} + +.bd-content .sd-card .sd-card-header { + border: none; + background-color: transparent; + color: var(--pst-color-text-base) !important; + font-size: var(--pst-font-size-h5); + font-weight: bold; + font-family: var(--pst-font-family-base); + padding: 0.5rem 0rem 0.5rem 0rem; +} + +.sd-card .sd-card-footer .sd-card-text { + max-width: 220px; + margin-left: auto; + margin-right: auto; + font-family: var(--pst-font-family-base); +} + +.sd-card-title { + font-family: var(--pst-font-family-base-system); +} + +.bd-content .sd-card .sd-card-body, +.bd-content .sd-card .sd-card-footer, +.bd-content .sd-card .sd-card-text { + background-color: transparent; +} + +html[data-theme="dark"] .sd-shadow-sm { + box-shadow: 0 0.1rem 1rem rgba(250, 250, 250, 0.6) !important; +} + +html[data-theme="dark"] .sd-card-img-top[src*=".png"] { + filter: invert(0.82) brightness(0.8) contrast(1.2); +} + +/* Common styles for all screen sizes */ +.sd-card { + border-radius: 0; + padding: 10px 10px 10px 10px; + font-family: var(--pst-font-family-base) !important; +} + +/* Media query for medium-sized screens */ +@media screen and (max-width: 768px) { + .sd-card .sd-card-header { + font-size: var(--pst-font-size-h6); + } +} + +/* Media query for small-sized screens */ +@media screen and (max-width: 576px) { + .sd-card .sd-card-header { + font-size: var(--pst-font-size-h5); + } + .sd-card { + padding: 5px 5px 5px 5px; + } + .sd-sphinx-override, + .sd-sphinx-override * { + box-sizing: content-box !important; + } +} + +/* +Sphinx-design dropdown +*/ + +.bd-content details.sd-dropdown .sd-summary-title { + border: 1px solid var(--pst-color-text-base) !important; + color: var(--pst-color-text-base) !important; + font-family: var(monospace) !important; + font-size: medium; + text-align: left; + padding-left: 1rem; +} + +details.sd-dropdown summary.sd-card-header + div.sd-summary-content { + background-color: transparent; +} + +/* +################################# +Right side toctree color and font +################################# +*/ +.toc-entry a.nav-link { + padding: 0.125rem 1.5rem; + color: var(--pst-color-link); +} + +.toc-entry a.nav-link.active { + font-weight: bold; + color: var(--pst-color-text-base); + background-color: transparent; + border-left: 2px solid var(--pst-color-text-muted); +} + +.toc-h2 { + font-size: 0.98rem; + padding: 0.05em; +} + +.toc-h3 { + font-size: 0.96rem; +} + +.toc-h4 { + font-size: 0.9rem; +} + +.toc-entry a.nav-link:hover { + color: var(--pst-color-link); + text-decoration: none; + font-weight: 600; +} + +/* +########### +Directives +########### +*/ + +div.deprecated { + border-color: var(--pst-color-danger); + background-color: #dc354514; +} + +div.deprecated, +div.versionadded, +div.versionchanged { + background-color: transparent; +} + +.admonition, +div.admonition { + background-color: var(--pst-color-on-surface); +} + +/* Select only divisions that contain a dataframe, with enough specificity to override pydata css */ +div.nboutput + div.output_area.rendered_html.docutils.container:has(table.dataframe) { + background-color: transparent; +} + +aside.topic > p { + color: var(--pst-color-text-base) !important; +} + +/* +############ +Border lines +############ +*/ +.bd-sidebar-primary, +.bd-sidebar-secondary { + max-height: calc(100vh - var(--pst-header-height) - 1vh); +} + +/* +################# +search hide match +################# +*/ + +div#searchbox p.highlight-link a { + background-color: var(--pst-color-search-match); +} + +.header-article__inner { + padding: 0 1rem; +} + +.table > :not(caption) > * > * { + background-color: transparent !important; + color: var(--pst-color-text-base) !important; +} + +nav.bd-links li > a { + text-decoration: none; +} + +.bd-header .navbar-nav li a.nav-link:hover { + text-decoration: none; + font-weight: 600; +} + +a { + text-decoration: none; +} + +a:active, +a:visited, +a:visited:hover, +a:active:hover, +a:hover { + text-decoration: underline; + color: var(--pst-color-link); + font-weight: 500; +} + +a:visited, +a:visited:hover { + color: var(--pst-color-link-visted); +} + +/* Cheat sheet sidebar */ + +.sidebar-cheatsheets h4 { + font-weight: var(--pst-sidebar-header-font-weight) !important; + font-size: var(--pst-sidebar-header-font-size) !important; + margin-bottom: 0.5rem; +} + +.sidebar-cheatsheets a { + display: inline-flex; + border: 0.5px solid var(--pst-color-border); + padding: 0.25rem; + max-width: 80%; +} + +.sidebar-cheatsheets a:hover { + background-color: var(--pst-color-border); + color: var(--pst-color-text-base); +} + +.sidebar-cheatsheets img { + padding: 0rem; +} + +.bd-sidebar-primary { + padding: 0.5rem; +} + +/* Do not display object domain namespace when documenting objects. + + Example: class ansys.module.foo.Foo.method -> method + + */ + +.sig-prename.descclassname { + display: none; +} diff --git a/version/0.1/_static/css/breadcrumbs.css b/version/0.1/_static/css/breadcrumbs.css new file mode 100644 index 000000000..60fe0155f --- /dev/null +++ b/version/0.1/_static/css/breadcrumbs.css @@ -0,0 +1,70 @@ +/* Provided by the Sphinx base theme template at build time, +styles exclusively for the ansys-sphinx-theme classes. */ + +@import "ansys-sphinx-theme.css"; + +/* +############ +breadcrumbs +############ +*/ + +#breadcrumbs-spacer { + border-right: 10rem solid var(--pst-color-background); +} + +div.related > ul { + padding: 10px 0 20px 0; +} + +*, +:after, +:before { + box-sizing: border-box; +} + +/* +########################### +vesrion warning announcement +############################ +*/ + +#announcement_msg { + display: flex; + justify-content: center; + position: relative; + width: 100%; + padding: 0.5rem 12.5%; + text-align: center; +} + +#announcement_msg :after { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + background-color: rgb(223, 95, 114); + opacity: 0.2; + content: ""; + z-index: -1; +} + +#announcement_msg :empty { + display: none; +} + +#announcement_msg p { + font-weight: bold; + margin: auto; + color: black; +} + +html[data-theme="dark"] #announcement_msg :after { + background-color: lightpink; + opacity: 0.5; +} + +#announcement_msg a { + color: #1e6ddc; +} diff --git a/version/0.1/_static/css/meilisearch.css b/version/0.1/_static/css/meilisearch.css new file mode 100644 index 000000000..97fa91e1f --- /dev/null +++ b/version/0.1/_static/css/meilisearch.css @@ -0,0 +1,194 @@ +@import "https://cdn.jsdelivr.net/npm/docs-searchbar.js@latest/dist/cdn/docs-searchbar.min.css"; +@import "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"; + +div[data-ds-theme] .searchbox { + overflow-y: scroll; + margin: auto; +} + +.docs-searchbar-suggestion--category-header { + background-color: var(--pst-color-border); + border-radius: 7px; + text-align: left; +} + +/* Styles for screens with a width of 576px or less */ +@media screen and (max-width: 576px) { + div[data-ds-theme] .searchbox { + width: 100%; + max-width: 100%; + } + .bd-search input { + width: 100% !important; + } + + .index-select { + width: 30%; + } +} + +/* Styles for screens with a width of 1200px or less */ +@media screen and (min-width: 1200px) { + div[data-ds-theme] .searchbox { + width: 100%; + max-width: 100%; + } + + .bd-search input { + width: 600px !important; + } + .index-select { + width: 250px; + } +} +.dsb-suggestions { + width: 100%; + max-width: 140%; +} + +div[data-ds-theme] .meilisearch-autocomplete .dsb-dropdown-menu { + max-width: 200%; + min-width: 100%; + width: 140%; +} +div[data-ds-theme] .meilisearch-autocomplete .docs-searchbar-suggestion { + width: 100%; +} + +div[data-ds-theme] .searchbox input { + height: 32px; + border-radius: 8px; + font-size: 18px; + font-family: "Open Sans", sans-serif; + box-shadow: 0px 0px 8px darkgrey; +} + +.docs-searchbar-footer { + display: none; +} + +.docs-searchbar-footer { + display: none; +} + +[class*="docs-searchbar-suggestion"] { + text-decoration: none; +} + +.docs-searchbar-suggestion--highlight { + box-shadow: none !important; +} + +.container { + display: flex; + justify-content: center; + align-items: right; +} + +div[data-ds-theme] .meilisearch-autocomplete { + text-align: center; + color: var(--pst-color-text-base); +} + +#search-bar-input { + background-color: var(--pst-color-background); + color: var(--pst-color-text-base); + font-size: var(--pst-font-size-icon); + position: relative; + padding-left: 1rem; + outline-color: var(--pst-color-border); + margin-left: 1rem; +} + +.index-select { + color: var(--pst-color-text-base); + background: var(--pst-color-background); + height: 47px; + border: 1px solid var(--pst-color-border); + border-radius: 0.25rem; + font-size: 20px; + font-family: "Open Sans", sans-serif; + box-shadow: 0px 0px 20px var(--pst-color-border); + padding: 0 10px 0px 10px; + margin-left: 5px; +} + +div[data-ds-theme] + .meilisearch-autocomplete + .dsb-dropdown-menu + [class^="dsb-dataset-"] { + position: relative; + border: 1px solid #d9d9d9; + background: var(--pst-color-background); + border-radius: 4px; + padding: 0 8px 8px; +} +div[data-ds-theme] .meilisearch-autocomplete .dsb-dropdown-menu { + max-height: 600px !important; + overflow-y: auto !important; + border: 1px solid #ccc; +} + +div[data-ds-theme] .meilisearch-autocomplete .docs-searchbar-suggestion { + background: var(--pst-color-background); +} + +div[data-ds-theme] + .meilisearch-autocomplete + .docs-searchbar-suggestion--highlight { + color: var(--pst-color-info) !important; + font-weight: 900; + background: transparent; + padding: 0 0.05em; +} + +div[data-ds-theme] + .meilisearch-autocomplete + .docs-searchbar-suggestion--subcategory-column { + width: None; + text-align: left; +} + +div[data-ds-theme] + .meilisearch-autocomplete + .docs-searchbar-suggestion--content { + display: block; +} + +div[data-ds-theme] .meilisearch-autocomplete .docs-searchbar-suggestion--title { + margin-bottom: 4px; + color: var(--pst-color-text-base); + font-size: 0.9em; + font-weight: 700; + width: 100%; +} + +/* Styling the scrollbar */ +div[data-ds-theme] + .meilisearch-autocomplete + .dsb-dropdown-menu::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; +} + +div[data-ds-theme] + .meilisearch-autocomplete + .dsb-dropdown-menu::-webkit-scrollbar-thumb { + background: var(--pst-color-text-base); + border-radius: inherit; +} + +div[data-ds-theme] + .meilisearch-autocomplete + .dsb-dropdown-menu::-webkit-scrollbar-track { + background: var(--pst-color-background); +} + +.bd-search { + margin-bottom: 200px; + gap: 0em; + border: 0px solid var(--pst-color-border); +} +#search-icon { + font-size: 1.5rem; +} diff --git a/version/0.1/_static/design-tabs.js b/version/0.1/_static/design-tabs.js new file mode 100644 index 000000000..b25bd6a4f --- /dev/null +++ b/version/0.1/_static/design-tabs.js @@ -0,0 +1,101 @@ +// @ts-check + +// Extra JS capability for selected tabs to be synced +// The selection is stored in local storage so that it persists across page loads. + +/** + * @type {Record} + */ +let sd_id_to_elements = {}; +const storageKeyPrefix = "sphinx-design-tab-id-"; + +/** + * Create a key for a tab element. + * @param {HTMLElement} el - The tab element. + * @returns {[string, string, string] | null} - The key. + * + */ +function create_key(el) { + let syncId = el.getAttribute("data-sync-id"); + let syncGroup = el.getAttribute("data-sync-group"); + if (!syncId || !syncGroup) return null; + return [syncGroup, syncId, syncGroup + "--" + syncId]; +} + +/** + * Initialize the tab selection. + * + */ +function ready() { + // Find all tabs with sync data + + /** @type {string[]} */ + let groups = []; + + document.querySelectorAll(".sd-tab-label").forEach((label) => { + if (label instanceof HTMLElement) { + let data = create_key(label); + if (data) { + let [group, id, key] = data; + + // add click event listener + // @ts-ignore + label.onclick = onSDLabelClick; + + // store map of key to elements + if (!sd_id_to_elements[key]) { + sd_id_to_elements[key] = []; + } + sd_id_to_elements[key].push(label); + + if (groups.indexOf(group) === -1) { + groups.push(group); + // Check if a specific tab has been selected via URL parameter + const tabParam = new URLSearchParams(window.location.search).get( + group + ); + if (tabParam) { + console.log( + "sphinx-design: Selecting tab id for group '" + + group + + "' from URL parameter: " + + tabParam + ); + window.sessionStorage.setItem(storageKeyPrefix + group, tabParam); + } + } + + // Check is a specific tab has been selected previously + let previousId = window.sessionStorage.getItem( + storageKeyPrefix + group + ); + if (previousId === id) { + // console.log( + // "sphinx-design: Selecting tab from session storage: " + id + // ); + // @ts-ignore + label.previousElementSibling.checked = true; + } + } + } + }); +} + +/** + * Activate other tabs with the same sync id. + * + * @this {HTMLElement} - The element that was clicked. + */ +function onSDLabelClick() { + let data = create_key(this); + if (!data) return; + let [group, id, key] = data; + for (const label of sd_id_to_elements[key]) { + if (label === this) continue; + // @ts-ignore + label.previousElementSibling.checked = true; + } + window.sessionStorage.setItem(storageKeyPrefix + group, id); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/version/0.1/_static/doctools.js b/version/0.1/_static/doctools.js new file mode 100644 index 000000000..4d67807d1 --- /dev/null +++ b/version/0.1/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/version/0.1/_static/documentation_options.js b/version/0.1/_static/documentation_options.js new file mode 100644 index 000000000..a1a7f13ec --- /dev/null +++ b/version/0.1/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.1.3', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: true, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/version/0.1/_static/file.png b/version/0.1/_static/file.png new file mode 100644 index 000000000..a858a410e Binary files /dev/null and b/version/0.1/_static/file.png differ diff --git a/version/0.1/_static/fonts/SourceSansPro-Light.ttf b/version/0.1/_static/fonts/SourceSansPro-Light.ttf new file mode 100644 index 000000000..348871ac6 Binary files /dev/null and b/version/0.1/_static/fonts/SourceSansPro-Light.ttf differ diff --git a/version/0.1/_static/fonts/SourceSansPro-Regular.ttf b/version/0.1/_static/fonts/SourceSansPro-Regular.ttf new file mode 100644 index 000000000..b422bf432 Binary files /dev/null and b/version/0.1/_static/fonts/SourceSansPro-Regular.ttf differ diff --git a/version/0.1/_static/fonts/SourceSansPro-SemiBold.ttf b/version/0.1/_static/fonts/SourceSansPro-SemiBold.ttf new file mode 100644 index 000000000..2908e0d78 Binary files /dev/null and b/version/0.1/_static/fonts/SourceSansPro-SemiBold.ttf differ diff --git a/version/0.1/_static/js/download_target_blank.js b/version/0.1/_static/js/download_target_blank.js new file mode 100644 index 000000000..9f84c70f2 --- /dev/null +++ b/version/0.1/_static/js/download_target_blank.js @@ -0,0 +1,6 @@ +/* Add target="_blank" attribute to all hyperlinks generated by the ReST :download: directive. + * This will ensure the links open in a new tab. */ + +$(document).ready(function () { + $("a.download").attr("target", "_blank"); +}); diff --git a/version/0.1/_static/js/meilisearch_theme_wrap.js b/version/0.1/_static/js/meilisearch_theme_wrap.js new file mode 100644 index 000000000..4309eea52 --- /dev/null +++ b/version/0.1/_static/js/meilisearch_theme_wrap.js @@ -0,0 +1,62 @@ +require.config({ + paths: { + docsSearchBar: + "https://cdn.jsdelivr.net/npm/docs-searchbar.js@2.5.0/dist/cdn/docs-searchbar.min", + }, +}); + +require(["docsSearchBar"], function (docsSearchBar) { + document.body.style.overflow = "hidden !important"; + // Initialize the MeiliSearch bar with the given API key and host + var theSearchBar = docsSearchBar({ + hostUrl: HOST_URL, + apiKey: API_KEY, + indexUid: indexUid, + inputSelector: "#search-bar-input", + debug: true, // Set debug to true if you want to inspect the dropdown + meilisearchOptions: { + limit: 10, + }, + }); + + // Function to show the magnifier icon + function showMagnifierIcon() { + var searchIcon = document.getElementById("search-icon"); + searchIcon.classList.remove("fa-spinner", "fa-spin"); // Remove spinner classes + searchIcon.classList.add("fa-magnifying-glass"); // Add magnifier icon class + } + + // Function to show the spinner icon + function showSpinnerIcon() { + var searchIcon = document.getElementById("search-icon"); + if (searchIcon) { + searchIcon.classList.remove("fa-magnifying-glass"); // Remove magnifier icon class + searchIcon.classList.add("fa-spinner", "fa-spin"); // Add spinner classes + } + } + + document + .getElementById("search-bar-input") + .addEventListener("input", function () { + const inputValue = this.value.trim(); // Trim whitespace from input value + // Show the spinner icon only when there is input and no suggestions + if ( + inputValue && + document.querySelectorAll(".dsb-suggestion").length === 0 + ) { + showSpinnerIcon(); + } else { + // Hide the spinner icon when there are suggestions + showMagnifierIcon(); + } + }); + + // Listen for changes in the dropdown selector and update the index uid and suggestion accordingly + document + .getElementById("indexUidSelector") + .addEventListener("change", function () { + theSearchBar.indexUid = this.value; + theSearchBar.suggestionIndexUid = this.value; + theSearchBar.autocomplete.autocomplete.setVal(""); + }); +}); diff --git a/version/0.1/_static/js/table.js b/version/0.1/_static/js/table.js new file mode 100644 index 000000000..a080ead14 --- /dev/null +++ b/version/0.1/_static/js/table.js @@ -0,0 +1,3 @@ +$(document).ready(function () { + $("table.datatable").DataTable(); +}); diff --git a/version/0.1/_static/jupyterlite_badge_logo.svg b/version/0.1/_static/jupyterlite_badge_logo.svg new file mode 100644 index 000000000..5de36d7fd --- /dev/null +++ b/version/0.1/_static/jupyterlite_badge_logo.svg @@ -0,0 +1,3 @@ + + +launchlaunchlitelite \ No newline at end of file diff --git a/version/0.1/_static/language_data.js b/version/0.1/_static/language_data.js new file mode 100644 index 000000000..367b8ed81 --- /dev/null +++ b/version/0.1/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/version/0.1/_static/minus.png b/version/0.1/_static/minus.png new file mode 100644 index 000000000..d96755fda Binary files /dev/null and b/version/0.1/_static/minus.png differ diff --git a/version/0.1/_static/no_image.png b/version/0.1/_static/no_image.png new file mode 100644 index 000000000..8c2d48d5d Binary files /dev/null and b/version/0.1/_static/no_image.png differ diff --git a/version/0.1/_static/plus.png b/version/0.1/_static/plus.png new file mode 100644 index 000000000..7107cec93 Binary files /dev/null and b/version/0.1/_static/plus.png differ diff --git a/version/0.1/_static/pyansys-logo-black-cropped.png b/version/0.1/_static/pyansys-logo-black-cropped.png new file mode 100644 index 000000000..e28bc3dec Binary files /dev/null and b/version/0.1/_static/pyansys-logo-black-cropped.png differ diff --git a/version/0.1/_static/pyansys-logo-white-cropped.png b/version/0.1/_static/pyansys-logo-white-cropped.png new file mode 100644 index 000000000..91b50f98e Binary files /dev/null and b/version/0.1/_static/pyansys-logo-white-cropped.png differ diff --git a/version/0.1/_static/pyansys_dark.png b/version/0.1/_static/pyansys_dark.png new file mode 100644 index 000000000..b0ff9e198 Binary files /dev/null and b/version/0.1/_static/pyansys_dark.png differ diff --git a/version/0.1/_static/pyansys_dark_square.png b/version/0.1/_static/pyansys_dark_square.png new file mode 100644 index 000000000..2b271562b Binary files /dev/null and b/version/0.1/_static/pyansys_dark_square.png differ diff --git a/version/0.1/_static/pyansys_light.png b/version/0.1/_static/pyansys_light.png new file mode 100644 index 000000000..9f31babdb Binary files /dev/null and b/version/0.1/_static/pyansys_light.png differ diff --git a/version/0.1/_static/pyansys_light_square.png b/version/0.1/_static/pyansys_light_square.png new file mode 100644 index 000000000..3e0706a51 Binary files /dev/null and b/version/0.1/_static/pyansys_light_square.png differ diff --git a/version/0.1/_static/pygments.css b/version/0.1/_static/pygments.css new file mode 100644 index 000000000..5bbc1bec8 --- /dev/null +++ b/version/0.1/_static/pygments.css @@ -0,0 +1,160 @@ +html[data-theme="light"] .highlight pre { line-height: 125%; } +html[data-theme="light"] .highlight td.linenos .normal { color: #666666; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos { color: #666666; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight .hll { background-color: #ffffcc } +html[data-theme="light"] .highlight { background: #f0f0f0; } +html[data-theme="light"] .highlight .c { color: #60a0b0; font-style: italic } /* Comment */ +html[data-theme="light"] .highlight .err { border: 1px solid #FF0000 } /* Error */ +html[data-theme="light"] .highlight .k { color: #007020; font-weight: bold } /* Keyword */ +html[data-theme="light"] .highlight .o { color: #666666 } /* Operator */ +html[data-theme="light"] .highlight .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */ +html[data-theme="light"] .highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */ +html[data-theme="light"] .highlight .cp { color: #007020 } /* Comment.Preproc */ +html[data-theme="light"] .highlight .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */ +html[data-theme="light"] .highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */ +html[data-theme="light"] .highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */ +html[data-theme="light"] .highlight .gd { color: #A00000 } /* Generic.Deleted */ +html[data-theme="light"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="light"] .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +html[data-theme="light"] .highlight .gr { color: #FF0000 } /* Generic.Error */ +html[data-theme="light"] .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +html[data-theme="light"] .highlight .gi { color: #00A000 } /* Generic.Inserted */ +html[data-theme="light"] .highlight .go { color: #888888 } /* Generic.Output */ +html[data-theme="light"] .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +html[data-theme="light"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="light"] .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +html[data-theme="light"] .highlight .gt { color: #0044DD } /* Generic.Traceback */ +html[data-theme="light"] .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +html[data-theme="light"] .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +html[data-theme="light"] .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +html[data-theme="light"] .highlight .kp { color: #007020 } /* Keyword.Pseudo */ +html[data-theme="light"] .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +html[data-theme="light"] .highlight .kt { color: #902000 } /* Keyword.Type */ +html[data-theme="light"] .highlight .m { color: #40a070 } /* Literal.Number */ +html[data-theme="light"] .highlight .s { color: #4070a0 } /* Literal.String */ +html[data-theme="light"] .highlight .na { color: #4070a0 } /* Name.Attribute */ +html[data-theme="light"] .highlight .nb { color: #007020 } /* Name.Builtin */ +html[data-theme="light"] .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +html[data-theme="light"] .highlight .no { color: #60add5 } /* Name.Constant */ +html[data-theme="light"] .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +html[data-theme="light"] .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ +html[data-theme="light"] .highlight .ne { color: #007020 } /* Name.Exception */ +html[data-theme="light"] .highlight .nf { color: #06287e } /* Name.Function */ +html[data-theme="light"] .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ +html[data-theme="light"] .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +html[data-theme="light"] .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ +html[data-theme="light"] .highlight .nv { color: #bb60d5 } /* Name.Variable */ +html[data-theme="light"] .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ +html[data-theme="light"] .highlight .w { color: #bbbbbb } /* Text.Whitespace */ +html[data-theme="light"] .highlight .mb { color: #40a070 } /* Literal.Number.Bin */ +html[data-theme="light"] .highlight .mf { color: #40a070 } /* Literal.Number.Float */ +html[data-theme="light"] .highlight .mh { color: #40a070 } /* Literal.Number.Hex */ +html[data-theme="light"] .highlight .mi { color: #40a070 } /* Literal.Number.Integer */ +html[data-theme="light"] .highlight .mo { color: #40a070 } /* Literal.Number.Oct */ +html[data-theme="light"] .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ +html[data-theme="light"] .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ +html[data-theme="light"] .highlight .sc { color: #4070a0 } /* Literal.String.Char */ +html[data-theme="light"] .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ +html[data-theme="light"] .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +html[data-theme="light"] .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ +html[data-theme="light"] .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +html[data-theme="light"] .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ +html[data-theme="light"] .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +html[data-theme="light"] .highlight .sx { color: #c65d09 } /* Literal.String.Other */ +html[data-theme="light"] .highlight .sr { color: #235388 } /* Literal.String.Regex */ +html[data-theme="light"] .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ +html[data-theme="light"] .highlight .ss { color: #517918 } /* Literal.String.Symbol */ +html[data-theme="light"] .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +html[data-theme="light"] .highlight .fm { color: #06287e } /* Name.Function.Magic */ +html[data-theme="light"] .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ +html[data-theme="light"] .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ +html[data-theme="light"] .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ +html[data-theme="light"] .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ +html[data-theme="light"] .highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ +html[data-theme="dark"] .highlight pre { line-height: 125%; } +html[data-theme="dark"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight .hll { background-color: #49483e } +html[data-theme="dark"] .highlight { background: #272822; color: #f8f8f2 } +html[data-theme="dark"] .highlight .c { color: #959077 } /* Comment */ +html[data-theme="dark"] .highlight .err { color: #ed007e; background-color: #1e0010 } /* Error */ +html[data-theme="dark"] .highlight .esc { color: #f8f8f2 } /* Escape */ +html[data-theme="dark"] .highlight .g { color: #f8f8f2 } /* Generic */ +html[data-theme="dark"] .highlight .k { color: #66d9ef } /* Keyword */ +html[data-theme="dark"] .highlight .l { color: #ae81ff } /* Literal */ +html[data-theme="dark"] .highlight .n { color: #f8f8f2 } /* Name */ +html[data-theme="dark"] .highlight .o { color: #ff4689 } /* Operator */ +html[data-theme="dark"] .highlight .x { color: #f8f8f2 } /* Other */ +html[data-theme="dark"] .highlight .p { color: #f8f8f2 } /* Punctuation */ +html[data-theme="dark"] .highlight .ch { color: #959077 } /* Comment.Hashbang */ +html[data-theme="dark"] .highlight .cm { color: #959077 } /* Comment.Multiline */ +html[data-theme="dark"] .highlight .cp { color: #959077 } /* Comment.Preproc */ +html[data-theme="dark"] .highlight .cpf { color: #959077 } /* Comment.PreprocFile */ +html[data-theme="dark"] .highlight .c1 { color: #959077 } /* Comment.Single */ +html[data-theme="dark"] .highlight .cs { color: #959077 } /* Comment.Special */ +html[data-theme="dark"] .highlight .gd { color: #ff4689 } /* Generic.Deleted */ +html[data-theme="dark"] .highlight .ge { color: #f8f8f2; font-style: italic } /* Generic.Emph */ +html[data-theme="dark"] .highlight .ges { color: #f8f8f2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +html[data-theme="dark"] .highlight .gr { color: #f8f8f2 } /* Generic.Error */ +html[data-theme="dark"] .highlight .gh { color: #f8f8f2 } /* Generic.Heading */ +html[data-theme="dark"] .highlight .gi { color: #a6e22e } /* Generic.Inserted */ +html[data-theme="dark"] .highlight .go { color: #66d9ef } /* Generic.Output */ +html[data-theme="dark"] .highlight .gp { color: #ff4689; font-weight: bold } /* Generic.Prompt */ +html[data-theme="dark"] .highlight .gs { color: #f8f8f2; font-weight: bold } /* Generic.Strong */ +html[data-theme="dark"] .highlight .gu { color: #959077 } /* Generic.Subheading */ +html[data-theme="dark"] .highlight .gt { color: #f8f8f2 } /* Generic.Traceback */ +html[data-theme="dark"] .highlight .kc { color: #66d9ef } /* Keyword.Constant */ +html[data-theme="dark"] .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ +html[data-theme="dark"] .highlight .kn { color: #ff4689 } /* Keyword.Namespace */ +html[data-theme="dark"] .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ +html[data-theme="dark"] .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ +html[data-theme="dark"] .highlight .kt { color: #66d9ef } /* Keyword.Type */ +html[data-theme="dark"] .highlight .ld { color: #e6db74 } /* Literal.Date */ +html[data-theme="dark"] .highlight .m { color: #ae81ff } /* Literal.Number */ +html[data-theme="dark"] .highlight .s { color: #e6db74 } /* Literal.String */ +html[data-theme="dark"] .highlight .na { color: #a6e22e } /* Name.Attribute */ +html[data-theme="dark"] .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ +html[data-theme="dark"] .highlight .nc { color: #a6e22e } /* Name.Class */ +html[data-theme="dark"] .highlight .no { color: #66d9ef } /* Name.Constant */ +html[data-theme="dark"] .highlight .nd { color: #a6e22e } /* Name.Decorator */ +html[data-theme="dark"] .highlight .ni { color: #f8f8f2 } /* Name.Entity */ +html[data-theme="dark"] .highlight .ne { color: #a6e22e } /* Name.Exception */ +html[data-theme="dark"] .highlight .nf { color: #a6e22e } /* Name.Function */ +html[data-theme="dark"] .highlight .nl { color: #f8f8f2 } /* Name.Label */ +html[data-theme="dark"] .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ +html[data-theme="dark"] .highlight .nx { color: #a6e22e } /* Name.Other */ +html[data-theme="dark"] .highlight .py { color: #f8f8f2 } /* Name.Property */ +html[data-theme="dark"] .highlight .nt { color: #ff4689 } /* Name.Tag */ +html[data-theme="dark"] .highlight .nv { color: #f8f8f2 } /* Name.Variable */ +html[data-theme="dark"] .highlight .ow { color: #ff4689 } /* Operator.Word */ +html[data-theme="dark"] .highlight .pm { color: #f8f8f2 } /* Punctuation.Marker */ +html[data-theme="dark"] .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ +html[data-theme="dark"] .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ +html[data-theme="dark"] .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ +html[data-theme="dark"] .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ +html[data-theme="dark"] .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ +html[data-theme="dark"] .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ +html[data-theme="dark"] .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ +html[data-theme="dark"] .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ +html[data-theme="dark"] .highlight .sc { color: #e6db74 } /* Literal.String.Char */ +html[data-theme="dark"] .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ +html[data-theme="dark"] .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ +html[data-theme="dark"] .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ +html[data-theme="dark"] .highlight .se { color: #ae81ff } /* Literal.String.Escape */ +html[data-theme="dark"] .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ +html[data-theme="dark"] .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ +html[data-theme="dark"] .highlight .sx { color: #e6db74 } /* Literal.String.Other */ +html[data-theme="dark"] .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ +html[data-theme="dark"] .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ +html[data-theme="dark"] .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ +html[data-theme="dark"] .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ +html[data-theme="dark"] .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ +html[data-theme="dark"] .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ +html[data-theme="dark"] .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ +html[data-theme="dark"] .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ +html[data-theme="dark"] .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ +html[data-theme="dark"] .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/version/0.1/_static/scripts/bootstrap.js b/version/0.1/_static/scripts/bootstrap.js new file mode 100644 index 000000000..bda8a6027 --- /dev/null +++ b/version/0.1/_static/scripts/bootstrap.js @@ -0,0 +1,3 @@ +/*! For license information please see bootstrap.js.LICENSE.txt */ +(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{afterMain:()=>w,afterRead:()=>b,afterWrite:()=>T,applyStyles:()=>D,arrow:()=>G,auto:()=>r,basePlacements:()=>a,beforeMain:()=>v,beforeRead:()=>g,beforeWrite:()=>E,bottom:()=>n,clippingParents:()=>h,computeStyles:()=>et,createPopper:()=>St,createPopperBase:()=>Lt,createPopperLite:()=>Dt,detectOverflow:()=>gt,end:()=>c,eventListeners:()=>nt,flip:()=>_t,hide:()=>yt,left:()=>o,main:()=>y,modifierPhases:()=>C,offset:()=>wt,placements:()=>m,popper:()=>u,popperGenerator:()=>kt,popperOffsets:()=>Et,preventOverflow:()=>At,read:()=>_,reference:()=>f,right:()=>s,start:()=>l,top:()=>i,variationPlacements:()=>p,viewport:()=>d,write:()=>A});var i="top",n="bottom",s="right",o="left",r="auto",a=[i,n,s,o],l="start",c="end",h="clippingParents",d="viewport",u="popper",f="reference",p=a.reduce((function(t,e){return t.concat([e+"-"+l,e+"-"+c])}),[]),m=[].concat(a,[r]).reduce((function(t,e){return t.concat([e,e+"-"+l,e+"-"+c])}),[]),g="beforeRead",_="read",b="afterRead",v="beforeMain",y="main",w="afterMain",E="beforeWrite",A="write",T="afterWrite",C=[g,_,b,v,y,w,E,A,T];function O(t){return t?(t.nodeName||"").toLowerCase():null}function x(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function k(t){return t instanceof x(t).Element||t instanceof Element}function L(t){return t instanceof x(t).HTMLElement||t instanceof HTMLElement}function S(t){return"undefined"!=typeof ShadowRoot&&(t instanceof x(t).ShadowRoot||t instanceof ShadowRoot)}const D={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];L(s)&&O(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});L(n)&&O(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function $(t){return t.split("-")[0]}var I=Math.max,N=Math.min,P=Math.round;function M(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function j(){return!/^((?!chrome|android).)*safari/i.test(M())}function F(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&L(t)&&(s=t.offsetWidth>0&&P(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&P(n.height)/t.offsetHeight||1);var r=(k(t)?x(t):window).visualViewport,a=!j()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function H(t){var e=F(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function B(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&S(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function W(t){return x(t).getComputedStyle(t)}function z(t){return["table","td","th"].indexOf(O(t))>=0}function R(t){return((k(t)?t.ownerDocument:t.document)||window.document).documentElement}function q(t){return"html"===O(t)?t:t.assignedSlot||t.parentNode||(S(t)?t.host:null)||R(t)}function V(t){return L(t)&&"fixed"!==W(t).position?t.offsetParent:null}function Y(t){for(var e=x(t),i=V(t);i&&z(i)&&"static"===W(i).position;)i=V(i);return i&&("html"===O(i)||"body"===O(i)&&"static"===W(i).position)?e:i||function(t){var e=/firefox/i.test(M());if(/Trident/i.test(M())&&L(t)&&"fixed"===W(t).position)return null;var i=q(t);for(S(i)&&(i=i.host);L(i)&&["html","body"].indexOf(O(i))<0;){var n=W(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function K(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Q(t,e,i){return I(t,N(e,i))}function X(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function U(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const G={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,r=t.state,l=t.name,c=t.options,h=r.elements.arrow,d=r.modifiersData.popperOffsets,u=$(r.placement),f=K(u),p=[o,s].indexOf(u)>=0?"height":"width";if(h&&d){var m=function(t,e){return X("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:U(t,a))}(c.padding,r),g=H(h),_="y"===f?i:o,b="y"===f?n:s,v=r.rects.reference[p]+r.rects.reference[f]-d[f]-r.rects.popper[p],y=d[f]-r.rects.reference[f],w=Y(h),E=w?"y"===f?w.clientHeight||0:w.clientWidth||0:0,A=v/2-y/2,T=m[_],C=E-g[p]-m[b],O=E/2-g[p]/2+A,x=Q(T,O,C),k=f;r.modifiersData[l]=((e={})[k]=x,e.centerOffset=x-O,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&B(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function J(t){return t.split("-")[1]}var Z={top:"auto",right:"auto",bottom:"auto",left:"auto"};function tt(t){var e,r=t.popper,a=t.popperRect,l=t.placement,h=t.variation,d=t.offsets,u=t.position,f=t.gpuAcceleration,p=t.adaptive,m=t.roundOffsets,g=t.isFixed,_=d.x,b=void 0===_?0:_,v=d.y,y=void 0===v?0:v,w="function"==typeof m?m({x:b,y}):{x:b,y};b=w.x,y=w.y;var E=d.hasOwnProperty("x"),A=d.hasOwnProperty("y"),T=o,C=i,O=window;if(p){var k=Y(r),L="clientHeight",S="clientWidth";k===x(r)&&"static"!==W(k=R(r)).position&&"absolute"===u&&(L="scrollHeight",S="scrollWidth"),(l===i||(l===o||l===s)&&h===c)&&(C=n,y-=(g&&k===O&&O.visualViewport?O.visualViewport.height:k[L])-a.height,y*=f?1:-1),l!==o&&(l!==i&&l!==n||h!==c)||(T=s,b-=(g&&k===O&&O.visualViewport?O.visualViewport.width:k[S])-a.width,b*=f?1:-1)}var D,$=Object.assign({position:u},p&&Z),I=!0===m?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:P(i*s)/s||0,y:P(n*s)/s||0}}({x:b,y},x(r)):{x:b,y};return b=I.x,y=I.y,f?Object.assign({},$,((D={})[C]=A?"0":"",D[T]=E?"0":"",D.transform=(O.devicePixelRatio||1)<=1?"translate("+b+"px, "+y+"px)":"translate3d("+b+"px, "+y+"px, 0)",D)):Object.assign({},$,((e={})[C]=A?y+"px":"",e[T]=E?b+"px":"",e.transform="",e))}const et={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:$(e.placement),variation:J(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,tt(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,tt(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var it={passive:!0};const nt={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=x(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,it)})),a&&l.addEventListener("resize",i.update,it),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,it)})),a&&l.removeEventListener("resize",i.update,it)}},data:{}};var st={left:"right",right:"left",bottom:"top",top:"bottom"};function ot(t){return t.replace(/left|right|bottom|top/g,(function(t){return st[t]}))}var rt={start:"end",end:"start"};function at(t){return t.replace(/start|end/g,(function(t){return rt[t]}))}function lt(t){var e=x(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ct(t){return F(R(t)).left+lt(t).scrollLeft}function ht(t){var e=W(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function dt(t){return["html","body","#document"].indexOf(O(t))>=0?t.ownerDocument.body:L(t)&&ht(t)?t:dt(q(t))}function ut(t,e){var i;void 0===e&&(e=[]);var n=dt(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=x(n),r=s?[o].concat(o.visualViewport||[],ht(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ut(q(r)))}function ft(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function pt(t,e,i){return e===d?ft(function(t,e){var i=x(t),n=R(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=j();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+ct(t),y:l}}(t,i)):k(e)?function(t,e){var i=F(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):ft(function(t){var e,i=R(t),n=lt(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=I(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=I(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ct(t),l=-n.scrollTop;return"rtl"===W(s||i).direction&&(a+=I(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(R(t)))}function mt(t){var e,r=t.reference,a=t.element,h=t.placement,d=h?$(h):null,u=h?J(h):null,f=r.x+r.width/2-a.width/2,p=r.y+r.height/2-a.height/2;switch(d){case i:e={x:f,y:r.y-a.height};break;case n:e={x:f,y:r.y+r.height};break;case s:e={x:r.x+r.width,y:p};break;case o:e={x:r.x-a.width,y:p};break;default:e={x:r.x,y:r.y}}var m=d?K(d):null;if(null!=m){var g="y"===m?"height":"width";switch(u){case l:e[m]=e[m]-(r[g]/2-a[g]/2);break;case c:e[m]=e[m]+(r[g]/2-a[g]/2)}}return e}function gt(t,e){void 0===e&&(e={});var o=e,r=o.placement,l=void 0===r?t.placement:r,c=o.strategy,p=void 0===c?t.strategy:c,m=o.boundary,g=void 0===m?h:m,_=o.rootBoundary,b=void 0===_?d:_,v=o.elementContext,y=void 0===v?u:v,w=o.altBoundary,E=void 0!==w&&w,A=o.padding,T=void 0===A?0:A,C=X("number"!=typeof T?T:U(T,a)),x=y===u?f:u,S=t.rects.popper,D=t.elements[E?x:y],$=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ut(q(t)),i=["absolute","fixed"].indexOf(W(t).position)>=0&&L(t)?Y(t):t;return k(i)?e.filter((function(t){return k(t)&&B(t,i)&&"body"!==O(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=pt(t,i,n);return e.top=I(s.top,e.top),e.right=N(s.right,e.right),e.bottom=N(s.bottom,e.bottom),e.left=I(s.left,e.left),e}),pt(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(k(D)?D:D.contextElement||R(t.elements.popper),g,b,p),P=F(t.elements.reference),M=mt({reference:P,element:S,strategy:"absolute",placement:l}),j=ft(Object.assign({},S,M)),H=y===u?j:P,z={top:$.top-H.top+C.top,bottom:H.bottom-$.bottom+C.bottom,left:$.left-H.left+C.left,right:H.right-$.right+C.right},V=t.modifiersData.offset;if(y===u&&V){var K=V[l];Object.keys(z).forEach((function(t){var e=[s,n].indexOf(t)>=0?1:-1,o=[i,n].indexOf(t)>=0?"y":"x";z[t]+=K[o]*e}))}return z}const _t={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,c=t.options,h=t.name;if(!e.modifiersData[h]._skip){for(var d=c.mainAxis,u=void 0===d||d,f=c.altAxis,g=void 0===f||f,_=c.fallbackPlacements,b=c.padding,v=c.boundary,y=c.rootBoundary,w=c.altBoundary,E=c.flipVariations,A=void 0===E||E,T=c.allowedAutoPlacements,C=e.options.placement,O=$(C),x=_||(O!==C&&A?function(t){if($(t)===r)return[];var e=ot(t);return[at(t),e,at(e)]}(C):[ot(C)]),k=[C].concat(x).reduce((function(t,i){return t.concat($(i)===r?function(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,l=i.flipVariations,c=i.allowedAutoPlacements,h=void 0===c?m:c,d=J(n),u=d?l?p:p.filter((function(t){return J(t)===d})):a,f=u.filter((function(t){return h.indexOf(t)>=0}));0===f.length&&(f=u);var g=f.reduce((function(e,i){return e[i]=gt(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[$(i)],e}),{});return Object.keys(g).sort((function(t,e){return g[t]-g[e]}))}(e,{placement:i,boundary:v,rootBoundary:y,padding:b,flipVariations:A,allowedAutoPlacements:T}):i)}),[]),L=e.rects.reference,S=e.rects.popper,D=new Map,I=!0,N=k[0],P=0;P=0,B=H?"width":"height",W=gt(e,{placement:M,boundary:v,rootBoundary:y,altBoundary:w,padding:b}),z=H?F?s:o:F?n:i;L[B]>S[B]&&(z=ot(z));var R=ot(z),q=[];if(u&&q.push(W[j]<=0),g&&q.push(W[z]<=0,W[R]<=0),q.every((function(t){return t}))){N=M,I=!1;break}D.set(M,q)}if(I)for(var V=function(t){var e=k.find((function(e){var i=D.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return N=e,"break"},Y=A?3:1;Y>0&&"break"!==V(Y);Y--);e.placement!==N&&(e.modifiersData[h]._skip=!0,e.placement=N,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function bt(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function vt(t){return[i,s,n,o].some((function(e){return t[e]>=0}))}const yt={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=gt(e,{elementContext:"reference"}),a=gt(e,{altBoundary:!0}),l=bt(r,n),c=bt(a,s,o),h=vt(l),d=vt(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},wt={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,n=t.options,r=t.name,a=n.offset,l=void 0===a?[0,0]:a,c=m.reduce((function(t,n){return t[n]=function(t,e,n){var r=$(t),a=[o,i].indexOf(r)>=0?-1:1,l="function"==typeof n?n(Object.assign({},e,{placement:t})):n,c=l[0],h=l[1];return c=c||0,h=(h||0)*a,[o,s].indexOf(r)>=0?{x:h,y:c}:{x:c,y:h}}(n,e.rects,l),t}),{}),h=c[e.placement],d=h.x,u=h.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=d,e.modifiersData.popperOffsets.y+=u),e.modifiersData[r]=c}},Et={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=mt({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},At={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,r=t.options,a=t.name,c=r.mainAxis,h=void 0===c||c,d=r.altAxis,u=void 0!==d&&d,f=r.boundary,p=r.rootBoundary,m=r.altBoundary,g=r.padding,_=r.tether,b=void 0===_||_,v=r.tetherOffset,y=void 0===v?0:v,w=gt(e,{boundary:f,rootBoundary:p,padding:g,altBoundary:m}),E=$(e.placement),A=J(e.placement),T=!A,C=K(E),O="x"===C?"y":"x",x=e.modifiersData.popperOffsets,k=e.rects.reference,L=e.rects.popper,S="function"==typeof y?y(Object.assign({},e.rects,{placement:e.placement})):y,D="number"==typeof S?{mainAxis:S,altAxis:S}:Object.assign({mainAxis:0,altAxis:0},S),P=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,M={x:0,y:0};if(x){if(h){var j,F="y"===C?i:o,B="y"===C?n:s,W="y"===C?"height":"width",z=x[C],R=z+w[F],q=z-w[B],V=b?-L[W]/2:0,X=A===l?k[W]:L[W],U=A===l?-L[W]:-k[W],G=e.elements.arrow,Z=b&&G?H(G):{width:0,height:0},tt=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},et=tt[F],it=tt[B],nt=Q(0,k[W],Z[W]),st=T?k[W]/2-V-nt-et-D.mainAxis:X-nt-et-D.mainAxis,ot=T?-k[W]/2+V+nt+it+D.mainAxis:U+nt+it+D.mainAxis,rt=e.elements.arrow&&Y(e.elements.arrow),at=rt?"y"===C?rt.clientTop||0:rt.clientLeft||0:0,lt=null!=(j=null==P?void 0:P[C])?j:0,ct=z+ot-lt,ht=Q(b?N(R,z+st-lt-at):R,z,b?I(q,ct):q);x[C]=ht,M[C]=ht-z}if(u){var dt,ut="x"===C?i:o,ft="x"===C?n:s,pt=x[O],mt="y"===O?"height":"width",_t=pt+w[ut],bt=pt-w[ft],vt=-1!==[i,o].indexOf(E),yt=null!=(dt=null==P?void 0:P[O])?dt:0,wt=vt?_t:pt-k[mt]-L[mt]-yt+D.altAxis,Et=vt?pt+k[mt]+L[mt]-yt-D.altAxis:bt,At=b&&vt?function(t,e,i){var n=Q(t,e,i);return n>i?i:n}(wt,pt,Et):Q(b?wt:_t,pt,b?Et:bt);x[O]=At,M[O]=At-pt}e.modifiersData[a]=M}},requiresIfExists:["offset"]};function Tt(t,e,i){void 0===i&&(i=!1);var n,s,o=L(e),r=L(e)&&function(t){var e=t.getBoundingClientRect(),i=P(e.width)/t.offsetWidth||1,n=P(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=R(e),l=F(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==O(e)||ht(a))&&(c=(n=e)!==x(n)&&L(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:lt(n)),L(e)?((h=F(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=ct(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function Ct(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var Ot={placement:"bottom",modifiers:[],strategy:"absolute"};function xt(){for(var t=arguments.length,e=new Array(t),i=0;i$t.has(t)&&$t.get(t).get(e)||null,remove(t,e){if(!$t.has(t))return;const i=$t.get(t);i.delete(e),0===i.size&&$t.delete(t)}},Nt="transitionend",Pt=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),Mt=t=>{t.dispatchEvent(new Event(Nt))},jt=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),Ft=t=>jt(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(Pt(t)):null,Ht=t=>{if(!jt(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},Bt=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),Wt=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?Wt(t.parentNode):null},zt=()=>{},Rt=t=>{t.offsetHeight},qt=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Vt=[],Yt=()=>"rtl"===document.documentElement.dir,Kt=t=>{var e;e=()=>{const e=qt();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(Vt.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of Vt)t()})),Vt.push(e)):e()},Qt=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,Xt=(t,e,i=!0)=>{if(!i)return void Qt(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let s=!1;const o=({target:i})=>{i===e&&(s=!0,e.removeEventListener(Nt,o),Qt(t))};e.addEventListener(Nt,o),setTimeout((()=>{s||Mt(e)}),n)},Ut=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},Gt=/[^.]*(?=\..*)\.|.*/,Jt=/\..*/,Zt=/::\d+$/,te={};let ee=1;const ie={mouseenter:"mouseover",mouseleave:"mouseout"},ne=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function se(t,e){return e&&`${e}::${ee++}`||t.uidEvent||ee++}function oe(t){const e=se(t);return t.uidEvent=e,te[e]=te[e]||{},te[e]}function re(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function ae(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=de(t);return ne.has(o)||(o=t),[n,s,o]}function le(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=ae(e,i,n);if(e in ie){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=oe(t),c=l[a]||(l[a]={}),h=re(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=se(r,e.replace(Gt,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return fe(s,{delegateTarget:r}),n.oneOff&&ue.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return fe(n,{delegateTarget:t}),i.oneOff&&ue.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function ce(t,e,i,n,s){const o=re(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function he(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&ce(t,e,i,r.callable,r.delegationSelector)}function de(t){return t=t.replace(Jt,""),ie[t]||t}const ue={on(t,e,i,n){le(t,e,i,n,!1)},one(t,e,i,n){le(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=ae(e,i,n),a=r!==e,l=oe(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))he(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(Zt,"");a&&!e.includes(s)||ce(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;ce(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=qt();let s=null,o=!0,r=!0,a=!1;e!==de(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=fe(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function fe(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function pe(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function me(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const ge={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${me(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${me(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=pe(t.dataset[n])}return e},getDataAttribute:(t,e)=>pe(t.getAttribute(`data-bs-${me(e)}`))};class _e{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=jt(e)?ge.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...jt(e)?ge.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],o=jt(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${o}" but expected type "${s}".`)}var i}}class be extends _e{constructor(t,e){super(),(t=Ft(t))&&(this._element=t,this._config=this._getConfig(e),It.set(this._element,this.constructor.DATA_KEY,this))}dispose(){It.remove(this._element,this.constructor.DATA_KEY),ue.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){Xt(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return It.get(Ft(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.2"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const ve=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?Pt(i.trim()):null}return e},ye={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!Bt(t)&&Ht(t)))},getSelectorFromElement(t){const e=ve(t);return e&&ye.findOne(e)?e:null},getElementFromSelector(t){const e=ve(t);return e?ye.findOne(e):null},getMultipleElementsFromSelector(t){const e=ve(t);return e?ye.find(e):[]}},we=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;ue.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),Bt(this))return;const s=ye.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},Ee=".bs.alert",Ae=`close${Ee}`,Te=`closed${Ee}`;class Ce extends be{static get NAME(){return"alert"}close(){if(ue.trigger(this._element,Ae).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),ue.trigger(this._element,Te),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Ce.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}we(Ce,"close"),Kt(Ce);const Oe='[data-bs-toggle="button"]';class xe extends be{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=xe.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}ue.on(document,"click.bs.button.data-api",Oe,(t=>{t.preventDefault();const e=t.target.closest(Oe);xe.getOrCreateInstance(e).toggle()})),Kt(xe);const ke=".bs.swipe",Le=`touchstart${ke}`,Se=`touchmove${ke}`,De=`touchend${ke}`,$e=`pointerdown${ke}`,Ie=`pointerup${ke}`,Ne={endCallback:null,leftCallback:null,rightCallback:null},Pe={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Me extends _e{constructor(t,e){super(),this._element=t,t&&Me.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Ne}static get DefaultType(){return Pe}static get NAME(){return"swipe"}dispose(){ue.off(this._element,ke)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),Qt(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&Qt(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(ue.on(this._element,$e,(t=>this._start(t))),ue.on(this._element,Ie,(t=>this._end(t))),this._element.classList.add("pointer-event")):(ue.on(this._element,Le,(t=>this._start(t))),ue.on(this._element,Se,(t=>this._move(t))),ue.on(this._element,De,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const je=".bs.carousel",Fe=".data-api",He="next",Be="prev",We="left",ze="right",Re=`slide${je}`,qe=`slid${je}`,Ve=`keydown${je}`,Ye=`mouseenter${je}`,Ke=`mouseleave${je}`,Qe=`dragstart${je}`,Xe=`load${je}${Fe}`,Ue=`click${je}${Fe}`,Ge="carousel",Je="active",Ze=".active",ti=".carousel-item",ei=Ze+ti,ii={ArrowLeft:ze,ArrowRight:We},ni={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},si={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class oi extends be{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=ye.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===Ge&&this.cycle()}static get Default(){return ni}static get DefaultType(){return si}static get NAME(){return"carousel"}next(){this._slide(He)}nextWhenVisible(){!document.hidden&&Ht(this._element)&&this.next()}prev(){this._slide(Be)}pause(){this._isSliding&&Mt(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?ue.one(this._element,qe,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void ue.one(this._element,qe,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?He:Be;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&ue.on(this._element,Ve,(t=>this._keydown(t))),"hover"===this._config.pause&&(ue.on(this._element,Ye,(()=>this.pause())),ue.on(this._element,Ke,(()=>this._maybeEnableCycle()))),this._config.touch&&Me.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of ye.find(".carousel-item img",this._element))ue.on(t,Qe,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(We)),rightCallback:()=>this._slide(this._directionToOrder(ze)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new Me(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=ii[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=ye.findOne(Ze,this._indicatorsElement);e.classList.remove(Je),e.removeAttribute("aria-current");const i=ye.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(Je),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===He,s=e||Ut(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>ue.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(Re).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),Rt(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(Je),i.classList.remove(Je,c,l),this._isSliding=!1,r(qe)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return ye.findOne(ei,this._element)}_getItems(){return ye.find(ti,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return Yt()?t===We?Be:He:t===We?He:Be}_orderToDirection(t){return Yt()?t===Be?We:ze:t===Be?ze:We}static jQueryInterface(t){return this.each((function(){const e=oi.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}ue.on(document,Ue,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=ye.getElementFromSelector(this);if(!e||!e.classList.contains(Ge))return;t.preventDefault();const i=oi.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===ge.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),ue.on(window,Xe,(()=>{const t=ye.find('[data-bs-ride="carousel"]');for(const e of t)oi.getOrCreateInstance(e)})),Kt(oi);const ri=".bs.collapse",ai=`show${ri}`,li=`shown${ri}`,ci=`hide${ri}`,hi=`hidden${ri}`,di=`click${ri}.data-api`,ui="show",fi="collapse",pi="collapsing",mi=`:scope .${fi} .${fi}`,gi='[data-bs-toggle="collapse"]',_i={parent:null,toggle:!0},bi={parent:"(null|element)",toggle:"boolean"};class vi extends be{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=ye.find(gi);for(const t of i){const e=ye.getSelectorFromElement(t),i=ye.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return _i}static get DefaultType(){return bi}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>vi.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(ue.trigger(this._element,ai).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(fi),this._element.classList.add(pi),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(pi),this._element.classList.add(fi,ui),this._element.style[e]="",ue.trigger(this._element,li)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(ue.trigger(this._element,ci).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,Rt(this._element),this._element.classList.add(pi),this._element.classList.remove(fi,ui);for(const t of this._triggerArray){const e=ye.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(pi),this._element.classList.add(fi),ue.trigger(this._element,hi)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(ui)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=Ft(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(gi);for(const e of t){const t=ye.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=ye.find(mi,this._config.parent);return ye.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=vi.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}ue.on(document,di,gi,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of ye.getMultipleElementsFromSelector(this))vi.getOrCreateInstance(t,{toggle:!1}).toggle()})),Kt(vi);const yi="dropdown",wi=".bs.dropdown",Ei=".data-api",Ai="ArrowUp",Ti="ArrowDown",Ci=`hide${wi}`,Oi=`hidden${wi}`,xi=`show${wi}`,ki=`shown${wi}`,Li=`click${wi}${Ei}`,Si=`keydown${wi}${Ei}`,Di=`keyup${wi}${Ei}`,$i="show",Ii='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Ni=`${Ii}.${$i}`,Pi=".dropdown-menu",Mi=Yt()?"top-end":"top-start",ji=Yt()?"top-start":"top-end",Fi=Yt()?"bottom-end":"bottom-start",Hi=Yt()?"bottom-start":"bottom-end",Bi=Yt()?"left-start":"right-start",Wi=Yt()?"right-start":"left-start",zi={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Ri={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class qi extends be{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=ye.next(this._element,Pi)[0]||ye.prev(this._element,Pi)[0]||ye.findOne(Pi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return zi}static get DefaultType(){return Ri}static get NAME(){return yi}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Bt(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!ue.trigger(this._element,xi,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))ue.on(t,"mouseover",zt);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add($i),this._element.classList.add($i),ue.trigger(this._element,ki,t)}}hide(){if(Bt(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!ue.trigger(this._element,Ci,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))ue.off(t,"mouseover",zt);this._popper&&this._popper.destroy(),this._menu.classList.remove($i),this._element.classList.remove($i),this._element.setAttribute("aria-expanded","false"),ge.removeDataAttribute(this._menu,"popper"),ue.trigger(this._element,Oi,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!jt(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${yi.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===e)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let t=this._element;"parent"===this._config.reference?t=this._parent:jt(this._config.reference)?t=Ft(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const i=this._getPopperConfig();this._popper=St(t,this._menu,i)}_isShown(){return this._menu.classList.contains($i)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Bi;if(t.classList.contains("dropstart"))return Wi;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?ji:Mi:e?Hi:Fi}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(ge.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...Qt(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=ye.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>Ht(t)));i.length&&Ut(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=ye.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ai,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:ye.prev(this,Ii)[0]||ye.next(this,Ii)[0]||ye.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}ue.on(document,Si,Ii,qi.dataApiKeydownHandler),ue.on(document,Si,Pi,qi.dataApiKeydownHandler),ue.on(document,Li,qi.clearMenus),ue.on(document,Di,qi.clearMenus),ue.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),Kt(qi);const Vi="backdrop",Yi="show",Ki=`mousedown.bs.${Vi}`,Qi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Xi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends _e{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Qi}static get DefaultType(){return Xi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void Qt(t);this._append();const e=this._getElement();this._config.isAnimated&&Rt(e),e.classList.add(Yi),this._emulateAnimation((()=>{Qt(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Yi),this._emulateAnimation((()=>{this.dispose(),Qt(t)}))):Qt(t)}dispose(){this._isAppended&&(ue.off(this._element,Ki),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=Ft(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),ue.on(t,Ki,(()=>{Qt(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){Xt(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends _e{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),ue.off(document,Gi),ue.on(document,Ji,(t=>this._handleFocusin(t))),ue.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,ue.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=ye.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&ge.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=ge.getDataAttribute(t,e);null!==i?(ge.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(jt(t))e(t);else for(const i of ye.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",En="show",An="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends be{constructor(t,e){super(t,e),this._dialog=ye.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||ue.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(ue.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(En),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){ue.off(window,hn),ue.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=ye.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),Rt(this._element),this._element.classList.add(En),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,ue.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){ue.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),ue.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),ue.on(this._element,bn,(t=>{ue.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(wn),this._resetAdjustments(),this._scrollBar.reset(),ue.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(ue.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(An)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(An),this._queueCallback((()=>{this._element.classList.remove(An),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=Yt()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=Yt()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}ue.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=ye.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),ue.one(e,pn,(t=>{t.defaultPrevented||ue.one(e,fn,(()=>{Ht(this)&&this.focus()}))}));const i=ye.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),we(On),Kt(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,Mn=`hide${xn}`,jn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Bn=`click${xn}${kn}`,Wn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends be{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||ue.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),ue.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(ue.trigger(this._element,Mn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),ue.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():ue.trigger(this._element,jn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){ue.on(this._element,Wn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():ue.trigger(this._element,jn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}ue.on(document,Bn,'[data-bs-toggle="offcanvas"]',(function(t){const e=ye.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),Bt(this))return;ue.one(e,Fn,(()=>{Ht(this)&&this.focus()}));const i=ye.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),ue.on(window,Ln,(()=>{for(const t of ye.find(In))qn.getOrCreateInstance(t).show()})),ue.on(window,Hn,(()=>{for(const t of ye.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),we(qn),Kt(qn);const Vn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Yn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Kn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Qn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Yn.has(i)||Boolean(Kn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Xn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends _e{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Xn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=ye.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?jt(e)?this._putElementInTemplate(Ft(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Qn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return Qt(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:Yt()?"left":"right",BOTTOM:"bottom",LEFT:Yt()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends be{constructor(t,i){if(void 0===e)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,i),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),ue.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=ue.trigger(this._element,this.constructor.eventName("show")),e=(Wt(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),ue.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))ue.on(t,"mouseover",zt);this._queueCallback((()=>{ue.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!ue.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))ue.off(t,"mouseover",zt);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),ue.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=Qt(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return St(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return Qt(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...Qt(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)ue.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");ue.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),ue.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},ue.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=ge.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:Ft(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Kt(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Kt(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},Es={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class As extends be{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return Es}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=Ft(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(ue.off(this._config.target,ms),ue.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=ye.find(bs,this._config.target);for(const e of t){if(!e.hash||Bt(e))continue;const t=ye.findOne(decodeURI(e.hash),this._element);Ht(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),ue.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))ye.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of ye.parents(t,".nav, .list-group"))for(const t of ye.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=ye.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=As.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}ue.on(window,gs,(()=>{for(const t of ye.find('[data-bs-spy="scroll"]'))As.getOrCreateInstance(t)})),Kt(As);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",Ms="Home",js="End",Fs="active",Hs="fade",Bs="show",Ws=".dropdown-toggle",zs=`:not(${Ws})`,Rs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',qs=`.nav-link${zs}, .list-group-item${zs}, [role="tab"]${zs}, ${Rs}`,Vs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Ys extends be{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),ue.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?ue.trigger(e,Cs,{relatedTarget:t}):null;ue.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(ye.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),ue.trigger(t,ks,{relatedTarget:e})):t.classList.add(Bs)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(ye.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),ue.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Bs)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,Ms,js].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!Bt(t)));let i;if([Ms,js].includes(t.key))i=e[t.key===Ms?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=Ut(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Ys.getOrCreateInstance(i).show())}_getChildren(){return ye.find(qs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=ye.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=ye.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ws,Fs),n(".dropdown-menu",Bs),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(qs)?t:ye.findOne(qs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Ys.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}ue.on(document,Ls,Rs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),Bt(this)||Ys.getOrCreateInstance(this).show()})),ue.on(window,Ds,(()=>{for(const t of ye.find(Vs))Ys.getOrCreateInstance(t)})),Kt(Ys);const Ks=".bs.toast",Qs=`mouseover${Ks}`,Xs=`mouseout${Ks}`,Us=`focusin${Ks}`,Gs=`focusout${Ks}`,Js=`hide${Ks}`,Zs=`hidden${Ks}`,to=`show${Ks}`,eo=`shown${Ks}`,io="hide",no="show",so="showing",oo={animation:"boolean",autohide:"boolean",delay:"number"},ro={animation:!0,autohide:!0,delay:5e3};class ao extends be{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return ro}static get DefaultType(){return oo}static get NAME(){return"toast"}show(){ue.trigger(this._element,to).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(io),Rt(this._element),this._element.classList.add(no,so),this._queueCallback((()=>{this._element.classList.remove(so),ue.trigger(this._element,eo),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(ue.trigger(this._element,Js).defaultPrevented||(this._element.classList.add(so),this._queueCallback((()=>{this._element.classList.add(io),this._element.classList.remove(so,no),ue.trigger(this._element,Zs)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(no),super.dispose()}isShown(){return this._element.classList.contains(no)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){ue.on(this._element,Qs,(t=>this._onInteraction(t,!0))),ue.on(this._element,Xs,(t=>this._onInteraction(t,!1))),ue.on(this._element,Us,(t=>this._onInteraction(t,!0))),ue.on(this._element,Gs,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ao.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}function lo(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}we(ao),Kt(ao),lo((function(){[].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')).map((function(t){return new cs(t,{delay:{show:500,hide:100}})}))})),lo((function(){document.getElementById("pst-back-to-top").addEventListener("click",(function(){document.body.scrollTop=0,document.documentElement.scrollTop=0}))})),lo((function(){var t=document.getElementById("pst-back-to-top"),e=document.getElementsByClassName("bd-header")[0].getBoundingClientRect();window.addEventListener("scroll",(function(){this.oldScroll>this.scrollY&&this.scrollY>e.bottom?t.style.display="block":t.style.display="none",this.oldScroll=this.scrollY}))}))})(); +//# sourceMappingURL=bootstrap.js.map \ No newline at end of file diff --git a/version/0.1/_static/scripts/bootstrap.js.LICENSE.txt b/version/0.1/_static/scripts/bootstrap.js.LICENSE.txt new file mode 100644 index 000000000..10f979d07 --- /dev/null +++ b/version/0.1/_static/scripts/bootstrap.js.LICENSE.txt @@ -0,0 +1,5 @@ +/*! + * Bootstrap v5.3.2 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ diff --git a/version/0.1/_static/scripts/bootstrap.js.map b/version/0.1/_static/scripts/bootstrap.js.map new file mode 100644 index 000000000..e5bc15752 --- /dev/null +++ b/version/0.1/_static/scripts/bootstrap.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/bootstrap.js","mappings":"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","sources":["webpack://pydata_sphinx_theme/webpack/bootstrap","webpack://pydata_sphinx_theme/webpack/runtime/define property getters","webpack://pydata_sphinx_theme/webpack/runtime/hasOwnProperty shorthand","webpack://pydata_sphinx_theme/webpack/runtime/make namespace object","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/enums.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/applyStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getBasePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/math.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/userAgent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/contains.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/within.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/expandToHashMap.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/arrow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getVariation.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/computeStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/eventListeners.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/rectToClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/detectOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/flip.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/hide.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/offset.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getAltAxis.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/orderModifiers.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/createPopper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/debounce.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergeByName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper-lite.js","webpack://pydata_sphinx_theme/./node_modules/bootstrap/dist/js/bootstrap.esm.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/mixin.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/bootstrap.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","/*!\n * Bootstrap v5.3.2 (https://getbootstrap.com/)\n * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\nimport * as Popper from '@popperjs/core';\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map();\nconst Data = {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map());\n }\n const instanceMap = elementMap.get(element);\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n return;\n }\n instanceMap.set(key, instance);\n },\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null;\n }\n return null;\n },\n remove(element, key) {\n if (!elementMap.has(element)) {\n return;\n }\n const instanceMap = elementMap.get(element);\n instanceMap.delete(key);\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element);\n }\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000;\nconst MILLISECONDS_MULTIPLIER = 1000;\nconst TRANSITION_END = 'transitionend';\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`);\n }\n return selector;\n};\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`;\n }\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n};\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID);\n } while (document.getElementById(prefix));\n return prefix;\n};\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0;\n }\n\n // Get transition-duration of the element\n let {\n transitionDuration,\n transitionDelay\n } = window.getComputedStyle(element);\n const floatTransitionDuration = Number.parseFloat(transitionDuration);\n const floatTransitionDelay = Number.parseFloat(transitionDelay);\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0;\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0];\n transitionDelay = transitionDelay.split(',')[0];\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n};\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END));\n};\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false;\n }\n if (typeof object.jquery !== 'undefined') {\n object = object[0];\n }\n return typeof object.nodeType !== 'undefined';\n};\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object;\n }\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object));\n }\n return null;\n};\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false;\n }\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])');\n if (!closedDetails) {\n return elementIsVisible;\n }\n if (closedDetails !== element) {\n const summary = element.closest('summary');\n if (summary && summary.parentNode !== closedDetails) {\n return false;\n }\n if (summary === null) {\n return false;\n }\n }\n return elementIsVisible;\n};\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true;\n }\n if (element.classList.contains('disabled')) {\n return true;\n }\n if (typeof element.disabled !== 'undefined') {\n return element.disabled;\n }\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n};\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null;\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode();\n return root instanceof ShadowRoot ? root : null;\n }\n if (element instanceof ShadowRoot) {\n return element;\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null;\n }\n return findShadowRoot(element.parentNode);\n};\nconst noop = () => {};\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight; // eslint-disable-line no-unused-expressions\n};\n\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery;\n }\n return null;\n};\nconst DOMContentLoadedCallbacks = [];\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback();\n }\n });\n }\n DOMContentLoadedCallbacks.push(callback);\n } else {\n callback();\n }\n};\nconst isRTL = () => document.documentElement.dir === 'rtl';\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery();\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME;\n const JQUERY_NO_CONFLICT = $.fn[name];\n $.fn[name] = plugin.jQueryInterface;\n $.fn[name].Constructor = plugin;\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT;\n return plugin.jQueryInterface;\n };\n }\n });\n};\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;\n};\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback);\n return;\n }\n const durationPadding = 5;\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n let called = false;\n const handler = ({\n target\n }) => {\n if (target !== transitionElement) {\n return;\n }\n called = true;\n transitionElement.removeEventListener(TRANSITION_END, handler);\n execute(callback);\n };\n transitionElement.addEventListener(TRANSITION_END, handler);\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement);\n }\n }, emulatedDuration);\n};\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length;\n let index = list.indexOf(activeElement);\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n }\n index += shouldGetNext ? 1 : -1;\n if (isCycleAllowed) {\n index = (index + listLength) % listLength;\n }\n return list[Math.max(0, Math.min(index, listLength - 1))];\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\nconst stripNameRegex = /\\..*/;\nconst stripUidRegex = /::\\d+$/;\nconst eventRegistry = {}; // Events storage\nlet uidEvent = 1;\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n};\nconst nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n}\nfunction getElementEvents(element) {\n const uid = makeEventUid(element);\n element.uidEvent = uid;\n eventRegistry[uid] = eventRegistry[uid] || {};\n return eventRegistry[uid];\n}\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, {\n delegateTarget: element\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn);\n }\n return fn.apply(element, [event]);\n };\n}\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector);\n for (let {\n target\n } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue;\n }\n hydrateObj(event, {\n delegateTarget: target\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn);\n }\n return fn.apply(target, [event]);\n }\n }\n };\n}\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n}\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string';\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n let typeEvent = getTypeEvent(originalTypeEvent);\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent;\n }\n return [isDelegated, callable, typeEvent];\n}\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n return fn.call(this, event);\n }\n };\n };\n callable = wrapFunction(callable);\n }\n const events = getElementEvents(element);\n const handlers = events[typeEvent] || (events[typeEvent] = {});\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff;\n return;\n }\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n fn.delegationSelector = isDelegated ? handler : null;\n fn.callable = callable;\n fn.oneOff = oneOff;\n fn.uidEvent = uid;\n handlers[uid] = fn;\n element.addEventListener(typeEvent, fn, isDelegated);\n}\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector);\n if (!fn) {\n return;\n }\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n delete events[typeEvent][fn.uidEvent];\n}\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {};\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n}\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '');\n return customEvents[event] || event;\n}\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false);\n },\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true);\n },\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n const inNamespace = typeEvent !== originalTypeEvent;\n const events = getElementEvents(element);\n const storeElementEvent = events[typeEvent] || {};\n const isNamespace = originalTypeEvent.startsWith('.');\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return;\n }\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n return;\n }\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n }\n }\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '');\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n },\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null;\n }\n const $ = getjQuery();\n const typeEvent = getTypeEvent(event);\n const inNamespace = event !== typeEvent;\n let jQueryEvent = null;\n let bubbles = true;\n let nativeDispatch = true;\n let defaultPrevented = false;\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args);\n $(element).trigger(jQueryEvent);\n bubbles = !jQueryEvent.isPropagationStopped();\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n defaultPrevented = jQueryEvent.isDefaultPrevented();\n }\n const evt = hydrateObj(new Event(event, {\n bubbles,\n cancelable: true\n }), args);\n if (defaultPrevented) {\n evt.preventDefault();\n }\n if (nativeDispatch) {\n element.dispatchEvent(evt);\n }\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault();\n }\n return evt;\n }\n};\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value;\n } catch (_unused) {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value;\n }\n });\n }\n }\n return obj;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n if (value === Number(value).toString()) {\n return Number(value);\n }\n if (value === '' || value === 'null') {\n return null;\n }\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(decodeURIComponent(value));\n } catch (_unused) {\n return value;\n }\n}\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n}\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n },\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n },\n getDataAttributes(element) {\n if (!element) {\n return {};\n }\n const attributes = {};\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '');\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n attributes[pureKey] = normalizeData(element.dataset[key]);\n }\n return attributes;\n },\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {};\n }\n static get DefaultType() {\n return {};\n }\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!');\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n return config;\n }\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n };\n }\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property];\n const valueType = isElement(value) ? 'element' : toType(value);\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n }\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.2';\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super();\n element = getElement(element);\n if (!element) {\n return;\n }\n this._element = element;\n this._config = this._getConfig(config);\n Data.set(this._element, this.constructor.DATA_KEY, this);\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY);\n EventHandler.off(this._element, this.constructor.EVENT_KEY);\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null;\n }\n }\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated);\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY);\n }\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n }\n static get VERSION() {\n return VERSION;\n }\n static get DATA_KEY() {\n return `bs.${this.NAME}`;\n }\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`;\n }\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target');\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href');\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n return null;\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n }\n selector = hrefAttribute && hrefAttribute !== '#' ? parseSelector(hrefAttribute.trim()) : null;\n }\n return selector;\n};\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n },\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector);\n },\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector));\n },\n parents(element, selector) {\n const parents = [];\n let ancestor = element.parentNode.closest(selector);\n while (ancestor) {\n parents.push(ancestor);\n ancestor = ancestor.parentNode.closest(selector);\n }\n return parents;\n },\n prev(element, selector) {\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.matches(selector)) {\n return [previous];\n }\n previous = previous.previousElementSibling;\n }\n return [];\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling;\n while (next) {\n if (next.matches(selector)) {\n return [next];\n }\n next = next.nextElementSibling;\n }\n return [];\n },\n focusableChildren(element) {\n const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n },\n getSelectorFromElement(element) {\n const selector = getSelector(element);\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null;\n }\n return null;\n },\n getElementFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.findOne(selector) : null;\n },\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.find(selector) : [];\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n const name = component.NAME;\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);\n const instance = component.getOrCreateInstance(target);\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]();\n });\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$f = 'alert';\nconst DATA_KEY$a = 'bs.alert';\nconst EVENT_KEY$b = `.${DATA_KEY$a}`;\nconst EVENT_CLOSE = `close${EVENT_KEY$b}`;\nconst EVENT_CLOSED = `closed${EVENT_KEY$b}`;\nconst CLASS_NAME_FADE$5 = 'fade';\nconst CLASS_NAME_SHOW$8 = 'show';\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$f;\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n if (closeEvent.defaultPrevented) {\n return;\n }\n this._element.classList.remove(CLASS_NAME_SHOW$8);\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n }\n\n // Private\n _destroyElement() {\n this._element.remove();\n EventHandler.trigger(this._element, EVENT_CLOSED);\n this.dispose();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close');\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$e = 'button';\nconst DATA_KEY$9 = 'bs.button';\nconst EVENT_KEY$a = `.${DATA_KEY$9}`;\nconst DATA_API_KEY$6 = '.data-api';\nconst CLASS_NAME_ACTIVE$3 = 'active';\nconst SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\nconst EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$e;\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this);\n if (config === 'toggle') {\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n event.preventDefault();\n const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n const data = Button.getOrCreateInstance(button);\n data.toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$d = 'swipe';\nconst EVENT_KEY$9 = '.bs.swipe';\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\nconst POINTER_TYPE_TOUCH = 'touch';\nconst POINTER_TYPE_PEN = 'pen';\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event';\nconst SWIPE_THRESHOLD = 40;\nconst Default$c = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n};\nconst DefaultType$c = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n};\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super();\n this._element = element;\n if (!element || !Swipe.isSupported()) {\n return;\n }\n this._config = this._getConfig(config);\n this._deltaX = 0;\n this._supportPointerEvents = Boolean(window.PointerEvent);\n this._initEvents();\n }\n\n // Getters\n static get Default() {\n return Default$c;\n }\n static get DefaultType() {\n return DefaultType$c;\n }\n static get NAME() {\n return NAME$d;\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY$9);\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX;\n return;\n }\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX;\n }\n }\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX;\n }\n this._handleSwipe();\n execute(this._config.endCallback);\n }\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n }\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX);\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return;\n }\n const direction = absDeltaX / this._deltaX;\n this._deltaX = 0;\n if (!direction) {\n return;\n }\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n }\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n }\n }\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$c = 'carousel';\nconst DATA_KEY$8 = 'bs.carousel';\nconst EVENT_KEY$8 = `.${DATA_KEY$8}`;\nconst DATA_API_KEY$5 = '.data-api';\nconst ARROW_LEFT_KEY$1 = 'ArrowLeft';\nconst ARROW_RIGHT_KEY$1 = 'ArrowRight';\nconst TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next';\nconst ORDER_PREV = 'prev';\nconst DIRECTION_LEFT = 'left';\nconst DIRECTION_RIGHT = 'right';\nconst EVENT_SLIDE = `slide${EVENT_KEY$8}`;\nconst EVENT_SLID = `slid${EVENT_KEY$8}`;\nconst EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\nconst EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\nconst EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\nconst EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst CLASS_NAME_CAROUSEL = 'carousel';\nconst CLASS_NAME_ACTIVE$2 = 'active';\nconst CLASS_NAME_SLIDE = 'slide';\nconst CLASS_NAME_END = 'carousel-item-end';\nconst CLASS_NAME_START = 'carousel-item-start';\nconst CLASS_NAME_NEXT = 'carousel-item-next';\nconst CLASS_NAME_PREV = 'carousel-item-prev';\nconst SELECTOR_ACTIVE = '.active';\nconst SELECTOR_ITEM = '.carousel-item';\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\nconst SELECTOR_ITEM_IMG = '.carousel-item img';\nconst SELECTOR_INDICATORS = '.carousel-indicators';\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n};\nconst Default$b = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n};\nconst DefaultType$b = {\n interval: '(number|boolean)',\n // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._interval = null;\n this._activeElement = null;\n this._isSliding = false;\n this.touchTimeout = null;\n this._swipeHelper = null;\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n this._addEventListeners();\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$b;\n }\n static get DefaultType() {\n return DefaultType$b;\n }\n static get NAME() {\n return NAME$c;\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT);\n }\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next();\n }\n }\n prev() {\n this._slide(ORDER_PREV);\n }\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element);\n }\n this._clearInterval();\n }\n cycle() {\n this._clearInterval();\n this._updateInterval();\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n }\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n return;\n }\n this.cycle();\n }\n to(index) {\n const items = this._getItems();\n if (index > items.length - 1 || index < 0) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n return;\n }\n const activeIndex = this._getItemIndex(this._getActive());\n if (activeIndex === index) {\n return;\n }\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n this._slide(order, items[index]);\n }\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose();\n }\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval;\n return config;\n }\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n }\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n }\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners();\n }\n }\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n }\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return;\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause();\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout);\n }\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n };\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n };\n this._swipeHelper = new Swipe(this._element, swipeConfig);\n }\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return;\n }\n const direction = KEY_TO_DIRECTION[event.key];\n if (direction) {\n event.preventDefault();\n this._slide(this._directionToOrder(direction));\n }\n }\n _getItemIndex(element) {\n return this._getItems().indexOf(element);\n }\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return;\n }\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n activeIndicator.removeAttribute('aria-current');\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n newActiveIndicator.setAttribute('aria-current', 'true');\n }\n }\n _updateInterval() {\n const element = this._activeElement || this._getActive();\n if (!element) {\n return;\n }\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n this._config.interval = elementInterval || this._config.defaultInterval;\n }\n _slide(order, element = null) {\n if (this._isSliding) {\n return;\n }\n const activeElement = this._getActive();\n const isNext = order === ORDER_NEXT;\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n if (nextElement === activeElement) {\n return;\n }\n const nextElementIndex = this._getItemIndex(nextElement);\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n });\n };\n const slideEvent = triggerEvent(EVENT_SLIDE);\n if (slideEvent.defaultPrevented) {\n return;\n }\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return;\n }\n const isCycling = Boolean(this._interval);\n this.pause();\n this._isSliding = true;\n this._setActiveIndicatorElement(nextElementIndex);\n this._activeElement = nextElement;\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n nextElement.classList.add(orderClassName);\n reflow(nextElement);\n activeElement.classList.add(directionalClassName);\n nextElement.classList.add(directionalClassName);\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName);\n nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n this._isSliding = false;\n triggerEvent(EVENT_SLID);\n };\n this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n if (isCycling) {\n this.cycle();\n }\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE);\n }\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n }\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element);\n }\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n }\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n }\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config);\n if (typeof config === 'number') {\n data.to(config);\n return;\n }\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return;\n }\n event.preventDefault();\n const carousel = Carousel.getOrCreateInstance(target);\n const slideIndex = this.getAttribute('data-bs-slide-to');\n if (slideIndex) {\n carousel.to(slideIndex);\n carousel._maybeEnableCycle();\n return;\n }\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next();\n carousel._maybeEnableCycle();\n return;\n }\n carousel.prev();\n carousel._maybeEnableCycle();\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel);\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$b = 'collapse';\nconst DATA_KEY$7 = 'bs.collapse';\nconst EVENT_KEY$7 = `.${DATA_KEY$7}`;\nconst DATA_API_KEY$4 = '.data-api';\nconst EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\nconst EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\nconst EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\nconst EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\nconst EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\nconst CLASS_NAME_SHOW$7 = 'show';\nconst CLASS_NAME_COLLAPSE = 'collapse';\nconst CLASS_NAME_COLLAPSING = 'collapsing';\nconst CLASS_NAME_COLLAPSED = 'collapsed';\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\nconst WIDTH = 'width';\nconst HEIGHT = 'height';\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\nconst SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\nconst Default$a = {\n parent: null,\n toggle: true\n};\nconst DefaultType$a = {\n parent: '(null|element)',\n toggle: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isTransitioning = false;\n this._triggerArray = [];\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem);\n const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem);\n }\n }\n this._initializeChildren();\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n }\n if (this._config.toggle) {\n this.toggle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$a;\n }\n static get DefaultType() {\n return DefaultType$a;\n }\n static get NAME() {\n return NAME$b;\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide();\n } else {\n this.show();\n }\n }\n show() {\n if (this._isTransitioning || this._isShown()) {\n return;\n }\n let activeChildren = [];\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n toggle: false\n }));\n }\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n for (const activeInstance of activeChildren) {\n activeInstance.hide();\n }\n const dimension = this._getDimension();\n this._element.classList.remove(CLASS_NAME_COLLAPSE);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.style[dimension] = 0;\n this._addAriaAndCollapsedClass(this._triggerArray, true);\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n this._element.style[dimension] = '';\n EventHandler.trigger(this._element, EVENT_SHOWN$6);\n };\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n const scrollSize = `scroll${capitalizedDimension}`;\n this._queueCallback(complete, this._element, true);\n this._element.style[dimension] = `${this._element[scrollSize]}px`;\n }\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n const dimension = this._getDimension();\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger);\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false);\n }\n }\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE);\n EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n };\n this._element.style[dimension] = '';\n this._queueCallback(complete, this._element, true);\n }\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW$7);\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle); // Coerce string values\n config.parent = getElement(config.parent);\n return config;\n }\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n }\n _initializeChildren() {\n if (!this._config.parent) {\n return;\n }\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element);\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected));\n }\n }\n }\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n }\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return;\n }\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n element.setAttribute('aria-expanded', isOpen);\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {};\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false;\n }\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config);\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n event.preventDefault();\n }\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, {\n toggle: false\n }).toggle();\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$a = 'dropdown';\nconst DATA_KEY$6 = 'bs.dropdown';\nconst EVENT_KEY$6 = `.${DATA_KEY$6}`;\nconst DATA_API_KEY$3 = '.data-api';\nconst ESCAPE_KEY$2 = 'Escape';\nconst TAB_KEY$1 = 'Tab';\nconst ARROW_UP_KEY$1 = 'ArrowUp';\nconst ARROW_DOWN_KEY$1 = 'ArrowDown';\nconst RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\nconst EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\nconst EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\nconst EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\nconst EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst CLASS_NAME_SHOW$6 = 'show';\nconst CLASS_NAME_DROPUP = 'dropup';\nconst CLASS_NAME_DROPEND = 'dropend';\nconst CLASS_NAME_DROPSTART = 'dropstart';\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center';\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\nconst SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\nconst SELECTOR_MENU = '.dropdown-menu';\nconst SELECTOR_NAVBAR = '.navbar';\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav';\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\nconst PLACEMENT_TOPCENTER = 'top';\nconst PLACEMENT_BOTTOMCENTER = 'bottom';\nconst Default$9 = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n};\nconst DefaultType$9 = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n};\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._popper = null;\n this._parent = this._element.parentNode; // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n this._inNavbar = this._detectNavbar();\n }\n\n // Getters\n static get Default() {\n return Default$9;\n }\n static get DefaultType() {\n return DefaultType$9;\n }\n static get NAME() {\n return NAME$a;\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show();\n }\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n if (showEvent.defaultPrevented) {\n return;\n }\n this._createPopper();\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n this._element.focus();\n this._element.setAttribute('aria-expanded', true);\n this._menu.classList.add(CLASS_NAME_SHOW$6);\n this._element.classList.add(CLASS_NAME_SHOW$6);\n EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n }\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n this._completeHide(relatedTarget);\n }\n dispose() {\n if (this._popper) {\n this._popper.destroy();\n }\n super.dispose();\n }\n update() {\n this._inNavbar = this._detectNavbar();\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n if (this._popper) {\n this._popper.destroy();\n }\n this._menu.classList.remove(CLASS_NAME_SHOW$6);\n this._element.classList.remove(CLASS_NAME_SHOW$6);\n this._element.setAttribute('aria-expanded', 'false');\n Manipulator.removeDataAttribute(this._menu, 'popper');\n EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n }\n _getConfig(config) {\n config = super._getConfig(config);\n if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n }\n return config;\n }\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n }\n let referenceElement = this._element;\n if (this._config.reference === 'parent') {\n referenceElement = this._parent;\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference);\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference;\n }\n const popperConfig = this._getPopperConfig();\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig);\n }\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n }\n _getPlacement() {\n const parentDropdown = this._parent;\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER;\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n }\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n }\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null;\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n };\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }];\n }\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _selectMenuItem({\n key,\n target\n }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n if (!items.length) {\n return;\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n return;\n }\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle);\n if (!context || context._config.autoClose === false) {\n continue;\n }\n const composedPath = event.composedPath();\n const isMenuTarget = composedPath.includes(context._menu);\n if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n continue;\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue;\n }\n const relatedTarget = {\n relatedTarget: context._element\n };\n if (event.type === 'click') {\n relatedTarget.clickEvent = event;\n }\n context._completeHide(relatedTarget);\n }\n }\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName);\n const isEscapeEvent = event.key === ESCAPE_KEY$2;\n const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return;\n }\n if (isInput && !isEscapeEvent) {\n return;\n }\n event.preventDefault();\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n const instance = Dropdown.getOrCreateInstance(getToggleButton);\n if (isUpOrDownEvent) {\n event.stopPropagation();\n instance.show();\n instance._selectMenuItem(event);\n return;\n }\n if (instance._isShown()) {\n // else is escape and we check if it is shown\n event.stopPropagation();\n instance.hide();\n getToggleButton.focus();\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n event.preventDefault();\n Dropdown.getOrCreateInstance(this).toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$9 = 'backdrop';\nconst CLASS_NAME_FADE$4 = 'fade';\nconst CLASS_NAME_SHOW$5 = 'show';\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\nconst Default$8 = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true,\n // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n};\n\nconst DefaultType$8 = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n};\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isAppended = false;\n this._element = null;\n }\n\n // Getters\n static get Default() {\n return Default$8;\n }\n static get DefaultType() {\n return DefaultType$8;\n }\n static get NAME() {\n return NAME$9;\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._append();\n const element = this._getElement();\n if (this._config.isAnimated) {\n reflow(element);\n }\n element.classList.add(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n execute(callback);\n });\n }\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n this.dispose();\n execute(callback);\n });\n }\n dispose() {\n if (!this._isAppended) {\n return;\n }\n EventHandler.off(this._element, EVENT_MOUSEDOWN);\n this._element.remove();\n this._isAppended = false;\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div');\n backdrop.className = this._config.className;\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE$4);\n }\n this._element = backdrop;\n }\n return this._element;\n }\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement);\n return config;\n }\n _append() {\n if (this._isAppended) {\n return;\n }\n const element = this._getElement();\n this._config.rootElement.append(element);\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback);\n });\n this._isAppended = true;\n }\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$8 = 'focustrap';\nconst DATA_KEY$5 = 'bs.focustrap';\nconst EVENT_KEY$5 = `.${DATA_KEY$5}`;\nconst EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\nconst TAB_KEY = 'Tab';\nconst TAB_NAV_FORWARD = 'forward';\nconst TAB_NAV_BACKWARD = 'backward';\nconst Default$7 = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n};\n\nconst DefaultType$7 = {\n autofocus: 'boolean',\n trapElement: 'element'\n};\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isActive = false;\n this._lastTabNavDirection = null;\n }\n\n // Getters\n static get Default() {\n return Default$7;\n }\n static get DefaultType() {\n return DefaultType$7;\n }\n static get NAME() {\n return NAME$8;\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return;\n }\n if (this._config.autofocus) {\n this._config.trapElement.focus();\n }\n EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n this._isActive = true;\n }\n deactivate() {\n if (!this._isActive) {\n return;\n }\n this._isActive = false;\n EventHandler.off(document, EVENT_KEY$5);\n }\n\n // Private\n _handleFocusin(event) {\n const {\n trapElement\n } = this._config;\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return;\n }\n const elements = SelectorEngine.focusableChildren(trapElement);\n if (elements.length === 0) {\n trapElement.focus();\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus();\n } else {\n elements[0].focus();\n }\n }\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return;\n }\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\nconst SELECTOR_STICKY_CONTENT = '.sticky-top';\nconst PROPERTY_PADDING = 'padding-right';\nconst PROPERTY_MARGIN = 'margin-right';\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body;\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth;\n return Math.abs(window.innerWidth - documentWidth);\n }\n hide() {\n const width = this.getWidth();\n this._disableOverFlow();\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n }\n reset() {\n this._resetElementAttributes(this._element, 'overflow');\n this._resetElementAttributes(this._element, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n }\n isOverflowing() {\n return this.getWidth() > 0;\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow');\n this._element.style.overflow = 'hidden';\n }\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth();\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return;\n }\n this._saveInitialAttribute(element, styleProperty);\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty);\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue);\n }\n }\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty);\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty);\n return;\n }\n Manipulator.removeDataAttribute(element, styleProperty);\n element.style.setProperty(styleProperty, value);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector);\n return;\n }\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel);\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$7 = 'modal';\nconst DATA_KEY$4 = 'bs.modal';\nconst EVENT_KEY$4 = `.${DATA_KEY$4}`;\nconst DATA_API_KEY$2 = '.data-api';\nconst ESCAPE_KEY$1 = 'Escape';\nconst EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\nconst EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\nconst EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\nconst EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\nconst EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\nconst EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\nconst EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\nconst EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\nconst CLASS_NAME_OPEN = 'modal-open';\nconst CLASS_NAME_FADE$3 = 'fade';\nconst CLASS_NAME_SHOW$4 = 'show';\nconst CLASS_NAME_STATIC = 'modal-static';\nconst OPEN_SELECTOR$1 = '.modal.show';\nconst SELECTOR_DIALOG = '.modal-dialog';\nconst SELECTOR_MODAL_BODY = '.modal-body';\nconst SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\nconst Default$6 = {\n backdrop: true,\n focus: true,\n keyboard: true\n};\nconst DefaultType$6 = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._isShown = false;\n this._isTransitioning = false;\n this._scrollBar = new ScrollBarHelper();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$6;\n }\n static get DefaultType() {\n return DefaultType$6;\n }\n static get NAME() {\n return NAME$7;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._isTransitioning = true;\n this._scrollBar.hide();\n document.body.classList.add(CLASS_NAME_OPEN);\n this._adjustDialog();\n this._backdrop.show(() => this._showElement(relatedTarget));\n }\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._isShown = false;\n this._isTransitioning = true;\n this._focustrap.deactivate();\n this._element.classList.remove(CLASS_NAME_SHOW$4);\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n }\n dispose() {\n EventHandler.off(window, EVENT_KEY$4);\n EventHandler.off(this._dialog, EVENT_KEY$4);\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n handleUpdate() {\n this._adjustDialog();\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop),\n // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element);\n }\n this._element.style.display = 'block';\n this._element.removeAttribute('aria-hidden');\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.scrollTop = 0;\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n if (modalBody) {\n modalBody.scrollTop = 0;\n }\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_SHOW$4);\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate();\n }\n this._isTransitioning = false;\n EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n relatedTarget\n });\n };\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n if (event.key !== ESCAPE_KEY$1) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n this._triggerBackdropTransition();\n });\n EventHandler.on(window, EVENT_RESIZE$1, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog();\n }\n });\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return;\n }\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition();\n return;\n }\n if (this._config.backdrop) {\n this.hide();\n }\n });\n });\n }\n _hideModal() {\n this._element.style.display = 'none';\n this._element.setAttribute('aria-hidden', true);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n this._isTransitioning = false;\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN);\n this._resetAdjustments();\n this._scrollBar.reset();\n EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n });\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE$3);\n }\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n if (hideEvent.defaultPrevented) {\n return;\n }\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const initialOverflowY = this._element.style.overflowY;\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return;\n }\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden';\n }\n this._element.classList.add(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY;\n }, this._dialog);\n }, this._dialog);\n this._element.focus();\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const scrollbarWidth = this._scrollBar.getWidth();\n const isBodyOverflowing = scrollbarWidth > 0;\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n }\n _resetAdjustments() {\n this._element.style.paddingLeft = '';\n this._element.style.paddingRight = '';\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](relatedTarget);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$4, () => {\n if (isVisible(this)) {\n this.focus();\n }\n });\n });\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide();\n }\n const data = Modal.getOrCreateInstance(target);\n data.toggle(this);\n});\nenableDismissTrigger(Modal);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$6 = 'offcanvas';\nconst DATA_KEY$3 = 'bs.offcanvas';\nconst EVENT_KEY$3 = `.${DATA_KEY$3}`;\nconst DATA_API_KEY$1 = '.data-api';\nconst EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst ESCAPE_KEY = 'Escape';\nconst CLASS_NAME_SHOW$3 = 'show';\nconst CLASS_NAME_SHOWING$1 = 'showing';\nconst CLASS_NAME_HIDING = 'hiding';\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\nconst OPEN_SELECTOR = '.offcanvas.show';\nconst EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\nconst EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\nconst EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\nconst EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\nconst EVENT_RESIZE = `resize${EVENT_KEY$3}`;\nconst EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\nconst SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\nconst Default$5 = {\n backdrop: true,\n keyboard: true,\n scroll: false\n};\nconst DefaultType$5 = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isShown = false;\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$5;\n }\n static get DefaultType() {\n return DefaultType$5;\n }\n static get NAME() {\n return NAME$6;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._backdrop.show();\n if (!this._config.scroll) {\n new ScrollBarHelper().hide();\n }\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.classList.add(CLASS_NAME_SHOWING$1);\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate();\n }\n this._element.classList.add(CLASS_NAME_SHOW$3);\n this._element.classList.remove(CLASS_NAME_SHOWING$1);\n EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n relatedTarget\n });\n };\n this._queueCallback(completeCallBack, this._element, true);\n }\n hide() {\n if (!this._isShown) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._focustrap.deactivate();\n this._element.blur();\n this._isShown = false;\n this._element.classList.add(CLASS_NAME_HIDING);\n this._backdrop.hide();\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n if (!this._config.scroll) {\n new ScrollBarHelper().reset();\n }\n EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n };\n this._queueCallback(completeCallback, this._element, true);\n }\n dispose() {\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n return;\n }\n this.hide();\n };\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop);\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n });\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$3, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus();\n }\n });\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide();\n }\n const data = Offcanvas.getOrCreateInstance(target);\n data.toggle(this);\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show();\n }\n});\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide();\n }\n }\n});\nenableDismissTrigger(Offcanvas);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\nconst DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n};\n// js-docs-end allow-list\n\nconst uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase();\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));\n }\n return true;\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n};\nfunction sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml;\n }\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml);\n }\n const domParser = new window.DOMParser();\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase();\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove();\n continue;\n }\n const attributeList = [].concat(...element.attributes);\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName);\n }\n }\n }\n return createdDocument.body.innerHTML;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$5 = 'TemplateFactory';\nconst Default$4 = {\n allowList: DefaultAllowlist,\n content: {},\n // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
'\n};\nconst DefaultType$4 = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n};\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n};\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n }\n\n // Getters\n static get Default() {\n return Default$4;\n }\n static get DefaultType() {\n return DefaultType$4;\n }\n static get NAME() {\n return NAME$5;\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n }\n hasContent() {\n return this.getContent().length > 0;\n }\n changeContent(content) {\n this._checkContent(content);\n this._config.content = {\n ...this._config.content,\n ...content\n };\n return this;\n }\n toHtml() {\n const templateWrapper = document.createElement('div');\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector);\n }\n const template = templateWrapper.children[0];\n const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n if (extraClass) {\n template.classList.add(...extraClass.split(' '));\n }\n return template;\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config);\n this._checkContent(config.content);\n }\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({\n selector,\n entry: content\n }, DefaultContentType);\n }\n }\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template);\n if (!templateElement) {\n return;\n }\n content = this._resolvePossibleFunction(content);\n if (!content) {\n templateElement.remove();\n return;\n }\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement);\n return;\n }\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content);\n return;\n }\n templateElement.textContent = content;\n }\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this]);\n }\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = '';\n templateElement.append(element);\n return;\n }\n templateElement.textContent = element.textContent;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$4 = 'tooltip';\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\nconst CLASS_NAME_FADE$2 = 'fade';\nconst CLASS_NAME_MODAL = 'modal';\nconst CLASS_NAME_SHOW$2 = 'show';\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\nconst EVENT_MODAL_HIDE = 'hide.bs.modal';\nconst TRIGGER_HOVER = 'hover';\nconst TRIGGER_FOCUS = 'focus';\nconst TRIGGER_CLICK = 'click';\nconst TRIGGER_MANUAL = 'manual';\nconst EVENT_HIDE$2 = 'hide';\nconst EVENT_HIDDEN$2 = 'hidden';\nconst EVENT_SHOW$2 = 'show';\nconst EVENT_SHOWN$2 = 'shown';\nconst EVENT_INSERTED = 'inserted';\nconst EVENT_CLICK$1 = 'click';\nconst EVENT_FOCUSIN$1 = 'focusin';\nconst EVENT_FOCUSOUT$1 = 'focusout';\nconst EVENT_MOUSEENTER = 'mouseenter';\nconst EVENT_MOUSELEAVE = 'mouseleave';\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n};\nconst Default$3 = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
' + '
' + '
' + '
',\n title: '',\n trigger: 'hover focus'\n};\nconst DefaultType$3 = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n};\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n }\n super(element, config);\n\n // Private\n this._isEnabled = true;\n this._timeout = 0;\n this._isHovered = null;\n this._activeTrigger = {};\n this._popper = null;\n this._templateFactory = null;\n this._newContent = null;\n\n // Protected\n this.tip = null;\n this._setListeners();\n if (!this._config.selector) {\n this._fixTitle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$3;\n }\n static get DefaultType() {\n return DefaultType$3;\n }\n static get NAME() {\n return NAME$4;\n }\n\n // Public\n enable() {\n this._isEnabled = true;\n }\n disable() {\n this._isEnabled = false;\n }\n toggleEnabled() {\n this._isEnabled = !this._isEnabled;\n }\n toggle() {\n if (!this._isEnabled) {\n return;\n }\n this._activeTrigger.click = !this._activeTrigger.click;\n if (this._isShown()) {\n this._leave();\n return;\n }\n this._enter();\n }\n dispose() {\n clearTimeout(this._timeout);\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n }\n this._disposePopper();\n super.dispose();\n }\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements');\n }\n if (!(this._isWithContent() && this._isEnabled)) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n const shadowRoot = findShadowRoot(this._element);\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n if (showEvent.defaultPrevented || !isInTheDom) {\n return;\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper();\n const tip = this._getTipElement();\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n const {\n container\n } = this._config;\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip);\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n }\n this._popper = this._createPopper(tip);\n tip.classList.add(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n if (this._isHovered === false) {\n this._leave();\n }\n this._isHovered = false;\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n hide() {\n if (!this._isShown()) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n if (hideEvent.defaultPrevented) {\n return;\n }\n const tip = this._getTipElement();\n tip.classList.remove(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n this._activeTrigger[TRIGGER_CLICK] = false;\n this._activeTrigger[TRIGGER_FOCUS] = false;\n this._activeTrigger[TRIGGER_HOVER] = false;\n this._isHovered = null; // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return;\n }\n if (!this._isHovered) {\n this._disposePopper();\n }\n this._element.removeAttribute('aria-describedby');\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n update() {\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle());\n }\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n }\n return this.tip;\n }\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml();\n\n // TODO: remove this check in v6\n if (!tip) {\n return null;\n }\n tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2);\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n const tipId = getUID(this.constructor.NAME).toString();\n tip.setAttribute('id', tipId);\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE$2);\n }\n return tip;\n }\n setContent(content) {\n this._newContent = content;\n if (this._isShown()) {\n this._disposePopper();\n this.show();\n }\n }\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content);\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n });\n }\n return this._templateFactory;\n }\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n };\n }\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n }\n _isAnimated() {\n return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n }\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n }\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element]);\n const attachment = AttachmentMap[placement.toUpperCase()];\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment));\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element]);\n }\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [{\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }, {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n }, {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n }\n }]\n };\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _setListeners() {\n const triggers = this._config.trigger.split(' ');\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context.toggle();\n });\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n context._enter();\n });\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n context._leave();\n });\n }\n }\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide();\n }\n };\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n }\n _fixTitle() {\n const title = this._element.getAttribute('title');\n if (!title) {\n return;\n }\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title);\n }\n this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title');\n }\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true;\n return;\n }\n this._isHovered = true;\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show();\n }\n }, this._config.delay.show);\n }\n _leave() {\n if (this._isWithActiveTrigger()) {\n return;\n }\n this._isHovered = false;\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide();\n }\n }, this._config.delay.hide);\n }\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout);\n this._timeout = setTimeout(handler, timeout);\n }\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true);\n }\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element);\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute];\n }\n }\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n };\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container);\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n };\n }\n if (typeof config.title === 'number') {\n config.title = config.title.toString();\n }\n if (typeof config.content === 'number') {\n config.content = config.content.toString();\n }\n return config;\n }\n _getDelegateConfig() {\n const config = {};\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value;\n }\n }\n config.selector = false;\n config.trigger = 'manual';\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config;\n }\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy();\n this._popper = null;\n }\n if (this.tip) {\n this.tip.remove();\n this.tip = null;\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$3 = 'popover';\nconst SELECTOR_TITLE = '.popover-header';\nconst SELECTOR_CONTENT = '.popover-body';\nconst Default$2 = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
' + '
' + '

' + '
' + '
',\n trigger: 'click'\n};\nconst DefaultType$2 = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n};\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default$2;\n }\n static get DefaultType() {\n return DefaultType$2;\n }\n static get NAME() {\n return NAME$3;\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent();\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n };\n }\n _getContent() {\n return this._resolvePossibleFunction(this._config.content);\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$2 = 'scrollspy';\nconst DATA_KEY$2 = 'bs.scrollspy';\nconst EVENT_KEY$2 = `.${DATA_KEY$2}`;\nconst DATA_API_KEY = '.data-api';\nconst EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\nconst EVENT_CLICK = `click${EVENT_KEY$2}`;\nconst EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\nconst CLASS_NAME_ACTIVE$1 = 'active';\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\nconst SELECTOR_TARGET_LINKS = '[href]';\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\nconst SELECTOR_NAV_LINKS = '.nav-link';\nconst SELECTOR_NAV_ITEMS = '.nav-item';\nconst SELECTOR_LIST_ITEMS = '.list-group-item';\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\nconst SELECTOR_DROPDOWN = '.dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\nconst Default$1 = {\n offset: null,\n // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n};\nconst DefaultType$1 = {\n offset: '(number|null)',\n // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n};\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map();\n this._observableSections = new Map();\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n this._activeTarget = null;\n this._observer = null;\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n };\n this.refresh(); // initialize\n }\n\n // Getters\n static get Default() {\n return Default$1;\n }\n static get DefaultType() {\n return DefaultType$1;\n }\n static get NAME() {\n return NAME$2;\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables();\n this._maybeEnableSmoothScroll();\n if (this._observer) {\n this._observer.disconnect();\n } else {\n this._observer = this._getNewObserver();\n }\n for (const section of this._observableSections.values()) {\n this._observer.observe(section);\n }\n }\n dispose() {\n this._observer.disconnect();\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body;\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n }\n return config;\n }\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return;\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK);\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash);\n if (observableSection) {\n event.preventDefault();\n const root = this._rootElement || window;\n const height = observableSection.offsetTop - this._element.offsetTop;\n if (root.scrollTo) {\n root.scrollTo({\n top: height,\n behavior: 'smooth'\n });\n return;\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height;\n }\n });\n }\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n };\n return new IntersectionObserver(entries => this._observerCallback(entries), options);\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n this._process(targetElement(entry));\n };\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n this._previousScrollData.parentScrollTop = parentScrollTop;\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null;\n this._clearActiveClass(targetElement(entry));\n continue;\n }\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry);\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return;\n }\n continue;\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry);\n }\n }\n }\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map();\n this._observableSections = new Map();\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue;\n }\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor);\n this._observableSections.set(anchor.hash, observableSection);\n }\n }\n }\n _process(target) {\n if (this._activeTarget === target) {\n return;\n }\n this._clearActiveClass(this._config.target);\n this._activeTarget = target;\n target.classList.add(CLASS_NAME_ACTIVE$1);\n this._activateParents(target);\n EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n relatedTarget: target\n });\n }\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n return;\n }\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both