├── .gitignore
├── LICENSE
├── README.md
├── data
├── README.md
├── by-sa.png
├── cc-zero.png
├── general.png
├── publicdomain.png
├── rir_clap.wav
├── sfa
│ ├── noise_rec_one.wav
│ ├── noise_rec_two.wav
│ └── xmas_noise_rec.wav
├── sfs
│ ├── sfs_continuous.png
│ └── sfs_discrete.png
├── volume.png
├── wooden_boards.jpg
└── xmas.wav
├── detailed_packages_list_yml.txt
├── farfield_directivity_ula.ipynb
├── index.ipynb
├── intro-solutions.ipynb
├── intro.ipynb
├── linear_systems_I-solutions.ipynb
├── linear_systems_I.ipynb
├── linear_systems_II-solutions.ipynb
├── linear_systems_II.ipynb
├── loudspeakers.ipynb
├── microphones-solutions.ipynb
├── microphones.ipynb
├── physics_of_sound_I-solutions.ipynb
├── physics_of_sound_I.ipynb
├── physics_of_sound_II-solutions.ipynb
├── physics_of_sound_II.ipynb
├── plot_direction.py
├── requirements.txt
├── sound_field_analysis.ipynb
├── sound_field_synthesis.ipynb
└── tools.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .ipynb_checkpoints/
2 | __pycache__/
3 | *.pyc
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Selected Topics in Audio Signal Processing - Exercises
2 | ======================================================
3 |
4 | - [static web pages (using nbviewer)](http://nbviewer.ipython.org/github/spatialaudio/selected-topics-in-audio-signal-processing-exercises/blob/master/index.ipynb)
5 | - [interactive web based (using mybinder)](https://mybinder.org/v2/gh/spatialaudio/selected-topics-in-audio-signal-processing-exercises/HEAD?filepath=index.ipynb)
6 |
7 | - for local usage create a conda environment (`mystiasp`), clone the git repository
8 | and start jupyter notebook or jupyter lab
9 | - created with `conda 4.9.2`
10 | - currently we rely on some older matplotlib version
11 | - `conda create -n mystiasp python=3.7.8 pip=20.2.4 numpy=1.19.4 scipy=1.5.3 matplotlib=3.1.3 jupyter=1.0.0 notebook=6.1.5 jupyterlab=2.2.9 pydocstyle=5.1.1 pycodestyle=2.6.0 autopep8=1.5.4 flake8=3.8.4 ipykernel=5.3.4 nb_conda=2.2.1 jupyter_nbextensions_configurator=0.4.1 jupyter_contrib_nbextensions=0.5.1`
12 | - `conda activate mystiasp`
13 | - we need to install pip packages since conda does not have them:
14 | - `pip install soundfile==0.10.3.post1`
15 | - `pip install sounddevice==0.4.1`
16 | - `python3 -m pip install sfs==0.5.0 --user`
17 | - make sure that the kernel is accesible for the notebooks:
18 | - `python -m ipykernel install --user --name mystiasp --display-name "mystiasp"`
19 | - now clone the repo:
20 | - `cd git` or whatever is a good root folder
21 | - `git clone https://github.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises.git`
22 | - `cd selected-topics-in-audio-signal-processing-exercises/`
23 | - `jupyter notebook index.ipynb` or `jupyter lab index.ipynb`
24 | - make sure that `mystiasp` kernel is used for the notebooks
25 | - have fun with the playgrounds
26 |
27 | Copyright
28 | ---------
29 |
30 | The authors waive copyright and related rights in the work through the
31 | [CC0 1.0 Universal public domain dedication](http://creativecommons.org/publicdomain/zero/1.0/).
32 |
33 | This is supposed to be an [Open Educational Resource](https://en.wikipedia.org/wiki/Open_educational_resources) (OER).
34 |
--------------------------------------------------------------------------------
/data/README.md:
--------------------------------------------------------------------------------
1 | Some Audio Files for Use in the Exercises
2 | =========================================
3 |
4 | Filename: [rir_clap.wav](rir_clap.wav)
5 | Description: The room impulse response (RIR) of a seminar room, measured by
6 | clapping two wooden boards together and recording the result with a
7 | microphone.
8 | Recorded with [Audacity][]
9 | [][CC0 1.0]
10 |
11 | Filename: [xmas.wav](xmas.wav)
12 | Description: "Wishing you a Merry Christmas, and a Happy New Year" (male voice)
13 | Source: http://www.freesound.org/people/davidbain/sounds/136777/
14 | [][CC0 1.0]
15 |
16 | Filenames: `xmas_*.wav`
17 | Description: Modifications of `xmas.wav` (see above).
18 | [][CC0 1.0]
19 |
20 | [Audacity]: http://audacityteam.org/
21 | [CC0 1.0]: http://creativecommons.org/publicdomain/zero/1.0/
22 | [CC BY-SA 3.0]: http://creativecommons.org/licenses/by-sa/3.0/
23 |
--------------------------------------------------------------------------------
/data/by-sa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/by-sa.png
--------------------------------------------------------------------------------
/data/cc-zero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/cc-zero.png
--------------------------------------------------------------------------------
/data/general.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/general.png
--------------------------------------------------------------------------------
/data/publicdomain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/publicdomain.png
--------------------------------------------------------------------------------
/data/rir_clap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/rir_clap.wav
--------------------------------------------------------------------------------
/data/sfa/noise_rec_one.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/sfa/noise_rec_one.wav
--------------------------------------------------------------------------------
/data/sfa/noise_rec_two.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/sfa/noise_rec_two.wav
--------------------------------------------------------------------------------
/data/sfa/xmas_noise_rec.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/sfa/xmas_noise_rec.wav
--------------------------------------------------------------------------------
/data/sfs/sfs_continuous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/sfs/sfs_continuous.png
--------------------------------------------------------------------------------
/data/sfs/sfs_discrete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/sfs/sfs_discrete.png
--------------------------------------------------------------------------------
/data/volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/volume.png
--------------------------------------------------------------------------------
/data/wooden_boards.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/wooden_boards.jpg
--------------------------------------------------------------------------------
/data/xmas.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/ca28a2abb13f0da39949fa2a5e770d2cb662f4b4/data/xmas.wav
--------------------------------------------------------------------------------
/detailed_packages_list_yml.txt:
--------------------------------------------------------------------------------
1 | name: mystiasp
2 | channels:
3 | - conda-forge
4 | - defaults
5 | dependencies:
6 | - appnope=0.1.0
7 | - argon2-cffi=20.1.0
8 | - async_generator=1.10
9 | - attrs=20.3.0
10 | - autopep8=1.5.4
11 | - backcall=0.2.0
12 | - backports=1.0
13 | - backports.functools_lru_cache=1.6.1
14 | - bleach=3.2.1
15 | - brotlipy=0.7.0
16 | - ca-certificates=2020.11.8
17 | - certifi=2020.11.8
18 | - cffi=1.14.4
19 | - chardet=3.0.4
20 | - cryptography=3.2.1
21 | - cycler=0.10.0
22 | - dbus=1.13.6
23 | - decorator=4.4.2
24 | - defusedxml=0.6.0
25 | - entrypoints=0.3
26 | - expat=2.2.9
27 | - flake8=3.8.4
28 | - freetype=2.10.4
29 | - gettext=0.19.8.1
30 | - glib=2.66.3
31 | - icu=67.1
32 | - idna=2.10
33 | - importlib-metadata=3.1.0
34 | - importlib_metadata=3.1.0
35 | - ipykernel=5.3.4
36 | - ipython=7.19.0
37 | - ipython_genutils=0.2.0
38 | - ipywidgets=7.5.1
39 | - jedi=0.17.2
40 | - jinja2=2.11.2
41 | - jpeg=9d
42 | - json5=0.9.5
43 | - jsonschema=3.2.0
44 | - jupyter=1.0.0
45 | - jupyter_client=6.1.7
46 | - jupyter_console=6.2.0
47 | - jupyter_contrib_core=0.3.3
48 | - jupyter_contrib_nbextensions=0.5.1
49 | - jupyter_core=4.7.0
50 | - jupyter_highlight_selected_word=0.2.0
51 | - jupyter_latex_envs=1.4.6
52 | - jupyter_nbextensions_configurator=0.4.1
53 | - jupyterlab=2.2.9
54 | - jupyterlab_pygments=0.1.2
55 | - jupyterlab_server=1.2.0
56 | - kiwisolver=1.3.1
57 | - krb5=1.17.2
58 | - libblas=3.9.0
59 | - libcblas=3.9.0
60 | - libclang=10.0.1
61 | - libcxx=11.0.0
62 | - libedit=3.1.20191231
63 | - libffi=3.2.1
64 | - libgfortran=5.0.0
65 | - libgfortran5=9.3.0
66 | - libglib=2.66.3
67 | - libiconv=1.16
68 | - liblapack=3.9.0
69 | - libllvm10=10.0.1
70 | - libopenblas=0.3.12
71 | - libpng=1.6.37
72 | - libpq=12.3
73 | - libsodium=1.0.18
74 | - libxml2=2.9.10
75 | - libxslt=1.1.33
76 | - llvm-openmp=11.0.0
77 | - lxml=4.6.1
78 | - lz4-c=1.9.2
79 | - markupsafe=1.1.1
80 | - matplotlib=3.1.3
81 | - matplotlib-base=3.1.3
82 | - mccabe=0.6.1
83 | - mistune=0.8.4
84 | - mysql-common=8.0.21
85 | - mysql-libs=8.0.21
86 | - nb_conda=2.2.1
87 | - nb_conda_kernels=2.3.0
88 | - nbclient=0.5.1
89 | - nbconvert=6.0.7
90 | - nbformat=5.0.8
91 | - ncurses=6.2
92 | - nest-asyncio=1.4.3
93 | - notebook=6.1.5
94 | - nspr=4.29
95 | - nss=3.47
96 | - numpy=1.19.4
97 | - openssl=1.1.1h
98 | - packaging=20.4
99 | - pandoc=2.11.2
100 | - pandocfilters=1.4.2
101 | - parso=0.7.1
102 | - pcre=8.44
103 | - pexpect=4.8.0
104 | - pickleshare=0.7.5
105 | - pip=20.2.4
106 | - prometheus_client=0.9.0
107 | - prompt-toolkit=3.0.8
108 | - prompt_toolkit=3.0.8
109 | - ptyprocess=0.6.0
110 | - pycodestyle=2.6.0
111 | - pycparser=2.20
112 | - pydocstyle=5.1.1
113 | - pyflakes=2.2.0
114 | - pygments=2.7.2
115 | - pyopenssl=19.1.0
116 | - pyparsing=2.4.7
117 | - pyqt=5.12.3
118 | - pyrsistent=0.17.3
119 | - pysocks=1.7.1
120 | - python=3.7.8
121 | - python-dateutil=2.8.1
122 | - python_abi=3.7
123 | - pyyaml=5.3.1
124 | - pyzmq=20.0.0
125 | - qt=5.12.9
126 | - qtconsole=5.0.1
127 | - qtpy=1.9.0
128 | - readline=8.0
129 | - requests=2.25.0
130 | - scipy=1.5.3
131 | - send2trash=1.5.0
132 | - setuptools=49.6.0
133 | - six=1.15.0
134 | - snowballstemmer=2.0.0
135 | - sqlite=3.33.0
136 | - terminado=0.9.1
137 | - testpath=0.4.4
138 | - tk=8.6.10
139 | - toml=0.10.2
140 | - tornado=6.1
141 | - traitlets=5.0.5
142 | - urllib3=1.25.11
143 | - wcwidth=0.2.5
144 | - webencodings=0.5.1
145 | - wheel=0.35.1
146 | - widgetsnbextension=3.5.1
147 | - xz=5.2.5
148 | - yaml=0.2.5
149 | - zeromq=4.3.3
150 | - zipp=3.4.0
151 | - zlib=1.2.11
152 | - zstd=1.4.5
153 | - pip:
154 | - pyqt5-sip==4.19.18
155 | - pyqtchart==5.12
156 | - pyqtwebengine==5.12.1
157 | - sfs==0.5.0
158 | - sounddevice==0.4.1
159 | - soundfile==0.10.3.post1
160 |
--------------------------------------------------------------------------------
/index.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Selected Topics in Audio Signal Processing - Exercises\n",
8 | "\n",
9 | "This is the main page for the exercises accompanying the lecture \"Selected Topics in Audio Signal Processing\" at [Institute of Communications Engineering](http://www.int.uni-rostock.de/)/[Faculty of Computer Science and Electrical Engineering](http://www.ief.uni-rostock.de/)/[University of Rostock](http://www.uni-rostock.de/).\n",
10 | "\n",
11 | "Registered students can access course details via [StudIP](https://studip.uni-rostock.de/dispatch.php/course/overview?cid=d5c4067c02bb446300d90ba54e5eb1fb) and [LSF](https://lsf.uni-rostock.de/qisserver/rds?state=verpublish&status=init&vmfile=no&moduleCall=webInfo&publishConfFile=webInfo&publishSubDir=veranstaltung&veranstaltung.veranstid=83421).\n",
12 | "\n",
13 | "The lectures/exercises are held each winter semester, starting in 2015.\n",
14 | "\n",
15 | "The notebooks and all additional files should be considered as [Open Educational Resources](https://en.wikipedia.org/wiki/Open_educational_resources)."
16 | ]
17 | },
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {},
21 | "source": [
22 | "## Table of Exercises\n",
23 | "\n",
24 | "The exercises are split into the following units.\n",
25 | "Most of them build upon knowledge from previous units, so you should do them in order:\n",
26 | "\n",
27 | "1. [Introduction to Python et al., Working with Audio Signals](intro.ipynb)\n",
28 | "2. [The Physics of Sound, Part I](physics_of_sound_I.ipynb)\n",
29 | "3. [The Physics of Sound, Part II](physics_of_sound_II.ipynb)\n",
30 | "4. [Linear Systems, Part I](linear_systems_I.ipynb)\n",
31 | "5. [Linear Systems, Part II](linear_systems_II.ipynb)\n",
32 | "6. [Microphones](microphones.ipynb)\n",
33 | "7. [Loudspeakers](loudspeakers.ipynb)\n",
34 | "8. [Sound Field Analysis](sound_field_analysis.ipynb)\n",
35 | "8. [Farfield Directivity Linear Array](farfield_directivity_ula.ipynb)\n",
36 | "9. [Sound Field Synthesis](sound_field_synthesis.ipynb)\n"
37 | ]
38 | },
39 | {
40 | "cell_type": "markdown",
41 | "metadata": {},
42 | "source": [
43 | "## Accessing the Exercises\n",
44 | "\n",
45 | "For the Exercises we use [jupyter notebooks](http://jupyter.org/). For accessing the notebooks, there are three alternatives:\n",
46 | "\n",
47 | "### Alternative 1: Static Web Pages\n",
48 | "\n",
49 | "The Jupyter notebooks for each topic are available as [static web pages](http://nbviewer.jupyter.org/github/spatialaudio/selected-topics-in-audio-signal-processing-exercises/). These are static html pages and you can't change them and try things out.\n",
50 | "\n",
51 | "### Alternative 2: Interactive Online Version \n",
52 | "\n",
53 | "If you don't feel like installing anything, but still want to try out the notebooks, you can\n",
54 | "\n",
55 | "[](http://mybinder.org/repo/fietew/selected-topics-in-audio-signal-processing-exercises) \n",
56 | "\n",
57 | "and start playing around immediately. Note, however, that your changes will not be preserved. Once you close your browser, everything will be lost!\n",
58 | "\n",
59 | "### Alternative 3: Use the Notebooks on your own Computer\n",
60 | "\n",
61 | "The most flexible alternative, as you can save your changes. However, you need to download and install some stuff in order to get things working on your machine.\n",
62 | "\n",
63 | "The information below might be outdated. Please have a look at readme.md\n",
64 | "\n",
65 | "https://github.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises\n",
66 | "\n",
67 | "for more recent installation guide using terminal (checked for Mac OS)\n",
68 | "\n",
69 | "#### Install Python \n",
70 | "\n",
71 | "Make sure you always use Python 3!\n",
72 | "\n",
73 | "##### ... under Windows\n",
74 | "For Windows, we recommend to download [Anaconda](https://www.continuum.io/downloads), a multi-platform the Python distribution (make sure to choose Python version 3.x).\n",
75 | "\n",
76 | "##### ... under Linux\n",
77 | "You can also use [Anaconda](https://www.continuum.io/downloads). As an alternative, you can use the following commands to install the necessary packages (Debian/Ubuntu/...):\n",
78 | "\n",
79 | " sudo apt-get update\n",
80 | " sudo apt-get install python3 python3-pip python3-numpy python3-scipy python3-matplotlib python3-cffi libsndfile1 libportaudio2\n",
81 | "\n",
82 | "#### Get the Notebooks and Start Jupyter\n",
83 | "\n",
84 | "Download the current [zip file](https://github.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises/archive/master.zip) (frequently updated) and extract it to an accessible directory. Alternatively, you can also download individual notebook files (with the extension `.ipynb`) from [github.com](https://github.com/spatialaudio/selected-topics-in-audio-signal-processing-exercises) and open them the Jupyter. Note, that some exercises make use of additional files (audio files etc.) which you'll then also have to download manually.\n",
85 | "\n",
86 | "##### ... under Windows\n",
87 | "\n",
88 | "Open the *Anaconda Prompt* (search for it the your start menu). Type the following commands (one after another):\n",
89 | "\n",
90 | " cd path/to/extracted/zip/file\n",
91 | " pip install -r requirements.txt\n",
92 | " python -m notebook\n",
93 | "\n",
94 | "First, this installs a few Python libraries that we will use. Afterwards, this will open a new view in your web browser. Click on [intro.ipynb](intro.ipynb) (or any of the other available notebooks) and enjoy!\n",
95 | "\n",
96 | "##### ... under Linux\n",
97 | "\n",
98 | "Use the Python package management system [pip](http://www.pip-installer.org/) to install a few Python libraries that we will use and then start the Jupyter notebook:\n",
99 | "\n",
100 | " cd path/to/extracted/zip/file\n",
101 | " python3 -m pip install -r requirements.txt --user\n",
102 | " python3 -m notebook\n",
103 | " \n",
104 | "This will open a new view in your web browser with a list of notebooks. Click on [intro.ipynb](intro.ipynb) (or any of the other available notebooks) and enjoy!"
105 | ]
106 | },
107 | {
108 | "cell_type": "markdown",
109 | "metadata": {},
110 | "source": [
111 | "## Copyright Information\n",
112 | "\n",
113 | "
\n",
114 | " \n",
116 | " \n",
117 | " \n",
118 | " \n",
119 | " To the extent possible under law,\n",
120 | " the person who associated CC0\n",
121 | " with this work has waived all copyright and related or neighboring\n",
122 | " rights to this work.\n",
123 | "
\n",
1164 | " \n",
1166 | " \n",
1167 | " \n",
1168 | " \n",
1169 | " To the extent possible under law,\n",
1170 | " the person who associated CC0\n",
1171 | " with this work has waived all copyright and related or neighboring\n",
1172 | " rights to this work.\n",
1173 | "
"
1174 | ]
1175 | }
1176 | ],
1177 | "metadata": {
1178 | "kernelspec": {
1179 | "display_name": "mystiasp",
1180 | "language": "python",
1181 | "name": "mystiasp"
1182 | },
1183 | "language_info": {
1184 | "codemirror_mode": {
1185 | "name": "ipython",
1186 | "version": 3
1187 | },
1188 | "file_extension": ".py",
1189 | "mimetype": "text/x-python",
1190 | "name": "python",
1191 | "nbconvert_exporter": "python",
1192 | "pygments_lexer": "ipython3",
1193 | "version": "3.7.8"
1194 | }
1195 | },
1196 | "nbformat": 4,
1197 | "nbformat_minor": 1
1198 | }
1199 |
--------------------------------------------------------------------------------
/linear_systems_I-solutions.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Linear Systems I - Solutions\n",
8 | "\n",
9 | "[back to exercise](linear_systems_I.ipynb)\n",
10 | "\n",
11 | "## Preparations"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": 1,
17 | "metadata": {
18 | "execution": {
19 | "iopub.execute_input": "2020-11-26T13:08:45.486618Z",
20 | "iopub.status.busy": "2020-11-26T13:08:45.486182Z",
21 | "iopub.status.idle": "2020-11-26T13:08:45.849175Z",
22 | "shell.execute_reply": "2020-11-26T13:08:45.849580Z"
23 | }
24 | },
25 | "outputs": [],
26 | "source": [
27 | "import tools\n",
28 | "import sounddevice as sd # for playback\n",
29 | "import soundfile as sf # for reading a soundfile"
30 | ]
31 | },
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {},
35 | "source": [
36 | "And some other stuff:"
37 | ]
38 | },
39 | {
40 | "cell_type": "code",
41 | "execution_count": 2,
42 | "metadata": {
43 | "execution": {
44 | "iopub.execute_input": "2020-11-26T13:08:45.852847Z",
45 | "iopub.status.busy": "2020-11-26T13:08:45.852392Z",
46 | "iopub.status.idle": "2020-11-26T13:08:46.000164Z",
47 | "shell.execute_reply": "2020-11-26T13:08:45.999709Z"
48 | }
49 | },
50 | "outputs": [],
51 | "source": [
52 | "# remove \"inline\" to get a separate plotting window:\n",
53 | "import numpy as np\n",
54 | "import matplotlib.pyplot as plt\n",
55 | "%matplotlib inline"
56 | ]
57 | },
58 | {
59 | "cell_type": "markdown",
60 | "metadata": {},
61 | "source": [
62 | "## One-dimensional time-continuous Systems\n",
63 | "\n",
64 | "First, we will have a quick review on linear systems in one dimension. In our case, we will use time signals depending on $t \\in \\mathbb{R}$. Generally, the input signal $x(t) \\in \\mathbb{C}$ and the corresponding output signal $y(t) \\in \\mathbb{C}$ of a system $\\mathcal H$ are related via:\n",
65 | "\n",
66 | "$$y(t) = \\mathcal{H}\\{x(t)\\}\\,.$$\n",
67 | "\n",
68 | "### Linear Time-Invariant (LTI) Systems\n",
69 | "\n",
70 | "As simple as it sounds, LTI-system are linear and time-invariant\n",
71 | "\n",
72 | "#### Linearity\n",
73 | "\n",
74 | "*Exercise*: Explain the term \"linear\" in your own words. \n",
75 | " \n",
76 | "\n",
77 | "*\n",
78 | "The system response (output signal) to a linear combination of different input signals is equal to the linear combination of the system responses to each invididual input signal\n",
79 | "*\n",
80 | " \n",
81 | "\n",
82 | "*Exercise*: What does this mean mathematically?\n",
83 | "\n",
84 | "\n",
85 | "$$\\mathcal{H}\\{A \\cdot x_1(t) + B \\cdot x_2(t)\\} = A \\cdot \\mathcal{H}\\{x_1(t)\\} + B \\cdot \\mathcal{H}\\{x_2(t)\\}\\, \\text{ for all } A,B \\in \\mathbb{C}$$\n",
86 | "\n"
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "metadata": {},
92 | "source": [
93 | "#### Time-Invariance\n",
94 | "\n",
95 | "*Exercise*: Explain the term \"time-invariance\" in your own words.\n",
96 | "\n",
97 | "*\n",
98 | "The properties of the system to not change over time: If a distinct input signal causes corresponding system response, a delayed version of that input signal results a delayed version of the system responses.\n",
99 | "*\n",
100 | "\n",
101 | "\n",
102 | "*Exercise*: What does this mean mathematically?\n",
103 | "\n",
104 | "\n",
105 | "$$\\mathcal{H}\\{x(t-\\tau)\\} = y(t-\\tau)\\, \\text{ for all } \\tau \\in \\mathbb{R}$$\n",
106 | "\n",
107 | "\n",
108 | "if $y(t) = \\mathcal{H} \\{ x(t) \\}$ is known.\n",
109 | "\n",
110 | "#### Are these systems LTI?\n",
111 | "\n",
112 | "*Exercise*: Vote for your LTI-system.\n",
113 | "\n",
114 | "1. $\\displaystyle y(t) = a \\cdot x(t) $ with $a \\in \\mathbb{C}$ \n",
115 | " Yes: \n",
116 | " No: \n",
117 | " Result: Yes\n",
118 | "2. $\\displaystyle y(t) = a \\cdot x(t) + b $ with $a,b \\in \\mathbb{C}$ \n",
119 | " Yes: \n",
120 | " No: \n",
121 | " Result: No: time-invariant but non-linear \n",
122 | "3. $\\displaystyle y(t) = a \\cdot x(t-t_0) $ with $a \\in \\mathbb{C}$ and $t_0 \\in \\mathbb{R}$ \n",
123 | " Yes: \n",
124 | " No: \n",
125 | " Result: Yes \n",
126 | "4. $\\displaystyle y(t) = a \\cdot x(t-b \\cdot t) $ with $a \\in \\mathbb{C}$ and $b \\in \\mathbb{R}$ \n",
127 | " Yes: \n",
128 | " No: \n",
129 | " Result: No: linear, but time-variant \n",
130 | "5. $\\displaystyle y(t) = \\frac{\\mathrm d x(t)}{\\mathrm d t}$ \n",
131 | " Yes: \n",
132 | " No: \n",
133 | " Result: Yes \n",
134 | "6. $\\displaystyle y(t) = \\int x(t)\\,\\mathrm d t $ \n",
135 | " Yes: \n",
136 | " No: \n",
137 | " Result: Yes \n",
138 | "7. $\\displaystyle y(t) = \\int_{-\\infty}^{\\infty} h(t_0) \\cdot x(t - t_0)\\,\\mathrm d t_0 $ \n",
139 | " Yes: \n",
140 | " No: \n",
141 | " Result: Yes\n",
142 | " \n",
143 | "#### Listen to a linear and a non-linear system\n",
144 | "\n",
145 | "We will investigate two unknown systems. The only information we have about these systems is that the first is LTI (linear and time invariant) and the second is non-linear. They are defined by the functions `tools.blackbox()` and `tools.blackbox_nonlinear()`. Have a quick look at the documentation:"
146 | ]
147 | },
148 | {
149 | "cell_type": "code",
150 | "execution_count": 3,
151 | "metadata": {
152 | "execution": {
153 | "iopub.execute_input": "2020-11-26T13:08:46.019826Z",
154 | "iopub.status.busy": "2020-11-26T13:08:46.019428Z",
155 | "iopub.status.idle": "2020-11-26T13:08:46.041818Z",
156 | "shell.execute_reply": "2020-11-26T13:08:46.042290Z"
157 | }
158 | },
159 | "outputs": [],
160 | "source": [
161 | "tools.blackbox?"
162 | ]
163 | },
164 | {
165 | "cell_type": "code",
166 | "execution_count": 4,
167 | "metadata": {
168 | "execution": {
169 | "iopub.execute_input": "2020-11-26T13:08:46.046371Z",
170 | "iopub.status.busy": "2020-11-26T13:08:46.045941Z",
171 | "iopub.status.idle": "2020-11-26T13:08:46.048446Z",
172 | "shell.execute_reply": "2020-11-26T13:08:46.048030Z"
173 | }
174 | },
175 | "outputs": [],
176 | "source": [
177 | "tools.blackbox_nonlinear?"
178 | ]
179 | },
180 | {
181 | "cell_type": "markdown",
182 | "metadata": {},
183 | "source": [
184 | "*Exercise:* Load the audio file [data/xmas.wav](data/xmas.wav) and apply both functions to it."
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": 5,
190 | "metadata": {
191 | "execution": {
192 | "iopub.execute_input": "2020-11-26T13:08:46.053254Z",
193 | "iopub.status.busy": "2020-11-26T13:08:46.052857Z",
194 | "iopub.status.idle": "2020-11-26T13:08:46.055205Z",
195 | "shell.execute_reply": "2020-11-26T13:08:46.054777Z"
196 | }
197 | },
198 | "outputs": [],
199 | "source": [
200 | "# how to read an audio file\n",
201 | "sf.read?"
202 | ]
203 | },
204 | {
205 | "cell_type": "code",
206 | "execution_count": 6,
207 | "metadata": {
208 | "execution": {
209 | "iopub.execute_input": "2020-11-26T13:08:46.058067Z",
210 | "iopub.status.busy": "2020-11-26T13:08:46.057618Z",
211 | "iopub.status.idle": "2020-11-26T13:08:46.073575Z",
212 | "shell.execute_reply": "2020-11-26T13:08:46.073977Z"
213 | }
214 | },
215 | "outputs": [],
216 | "source": [
217 | "[x, fs] = sf.read('data/xmas.wav')\n",
218 | "\n",
219 | "ylin = tools.blackbox(x, fs)\n",
220 | "ynonlin = tools.blackbox_nonlinear(x, fs)"
221 | ]
222 | },
223 | {
224 | "cell_type": "markdown",
225 | "metadata": {},
226 | "source": [
227 | "*Exercise*: Listen to the input signal and both output signals.\n",
228 | "\n",
229 | "**CAUTION: CHECK THE PLAYBACk LEVEL**"
230 | ]
231 | },
232 | {
233 | "cell_type": "code",
234 | "execution_count": 7,
235 | "metadata": {
236 | "execution": {
237 | "iopub.execute_input": "2020-11-26T13:08:46.078109Z",
238 | "iopub.status.busy": "2020-11-26T13:08:46.077670Z",
239 | "iopub.status.idle": "2020-11-26T13:08:46.079231Z",
240 | "shell.execute_reply": "2020-11-26T13:08:46.079653Z"
241 | }
242 | },
243 | "outputs": [],
244 | "source": [
245 | "# how to play back the signal\n",
246 | "sd.play?"
247 | ]
248 | },
249 | {
250 | "cell_type": "code",
251 | "execution_count": 8,
252 | "metadata": {
253 | "execution": {
254 | "iopub.execute_input": "2020-11-26T13:08:46.082755Z",
255 | "iopub.status.busy": "2020-11-26T13:08:46.082325Z",
256 | "iopub.status.idle": "2020-11-26T13:08:54.287531Z",
257 | "shell.execute_reply": "2020-11-26T13:08:54.287945Z"
258 | }
259 | },
260 | "outputs": [],
261 | "source": [
262 | "sd.play(x, blocking=True)\n",
263 | "sd.play(ylin, blocking=True)\n",
264 | "sd.play(ynonlin, blocking=True)"
265 | ]
266 | },
267 | {
268 | "cell_type": "markdown",
269 | "metadata": {},
270 | "source": [
271 | "### The Impulse Response\n",
272 | "\n",
273 | "The impulse response $h(t)$ of an LTI system characterises it completely. It is the LTI system's response to a Dirac impulse $\\delta(t)$\n",
274 | "\n",
275 | "$$h(t) = \\mathcal{H}\\{\\delta(t)\\}\\,.$$\n",
276 | "\n",
277 | "So why is the impulse response sufficient to describe the whole LTI system? The input signal $x(t)$ can be described as a sequence of Dirac impulses \n",
278 | "\n",
279 | "$$ x(t) = \\int_{-\\infty}^{\\infty} x(t_0) \\cdot \\delta(t-t_0)\\,\\mathrm d t_0\\,,$$ \n",
280 | "\n",
281 | "where the Dirac impulse at $t_0$ is weighted by the value $x(t_0)$ of the signal at $t_0$. Applying the system onto $x(t)$ yields\n",
282 | "\n",
283 | "$$ y(t) = \\mathcal{H}\\{x(t)\\} = \\mathcal{H}\\left\\{\\int_{-\\infty}^{\\infty} x(t_0) \\cdot \\delta(t-t_0)\\,\\mathrm d t_0\\right\\}$$\n",
284 | "\n",
285 | "As a next step, we can exchange the integral operator $\\int$ and the system operator $\\mathcal{H}$:\n",
286 | "\n",
287 | "$$ y(t) = \\mathcal{H}\\{x(t)\\} = \\int_{-\\infty}^{\\infty} x(t_0) \\cdot \\mathcal{H}\\{\\delta(t-t_0)\\}\\,\\mathrm d t_0$$\n",
288 | "\n",
289 | "*Exercise*: What property has to be fulfilled by $\\mathcal{H}$ in order to be able to exchange integral and system operator?"
290 | ]
291 | },
292 | {
293 | "cell_type": "markdown",
294 | "metadata": {},
295 | "source": [
296 | "\n",
297 | "*\n",
298 | "Linearity \n",
299 | "*\n",
300 | ""
301 | ]
302 | },
303 | {
304 | "cell_type": "markdown",
305 | "metadata": {},
306 | "source": [
307 | "As the last step, we re-write the system response of the Dirac $\\delta$ shifted about $t_0$ as the shifted impulse response $h(t-t_0)$:\n",
308 | "\n",
309 | "$$ y(t) = \\mathcal{H}\\{x(t)\\} = \\int_{-\\infty}^{\\infty} x(t_0) \\cdot h(t-t_0)\\,\\mathrm d t_0$$\n",
310 | "\n",
311 | "*Exercise*: What property has to be fulfilled by $\\mathcal{H}$ in order to replace $\\mathcal{H}\\{\\delta(t-t_0)\\}$ by $h(t-t_0)$?"
312 | ]
313 | },
314 | {
315 | "cell_type": "markdown",
316 | "metadata": {},
317 | "source": [
318 | "\n",
319 | "*\n",
320 | "Time-Invariance\n",
321 | "*\n",
322 | ""
323 | ]
324 | },
325 | {
326 | "cell_type": "markdown",
327 | "metadata": {},
328 | "source": [
329 | "Hence, we can describe the output signal $y(t)$ by the so-called **linear convolution** integral of the corresponding input signal $x(t)$ and the impulse response $h(t)$. Its short version reads\n",
330 | "\n",
331 | "$$ y(t) = x(t) * h(t) $$\n",
332 | "\n",
333 | "where $*$ is a common notation of the convolution."
334 | ]
335 | },
336 | {
337 | "cell_type": "markdown",
338 | "metadata": {},
339 | "source": [
340 | "### A Naive Implementation of the Linear Convolution\n",
341 | "\n",
342 | "Time-continuous signals cannot be handled by computers. Signals must be sampled in time with the sample period $T_s$.\n",
343 | "This yields discrete-time signals. The discrete-time counterpart of a linear convolution is given as\n",
344 | "\n",
345 | "$$ y[n] = x[n] \\ast h[n] = \\sum_{k = -\\infty}^{\\infty} x[k] \\cdot h[n-k] $$\n",
346 | "\n",
347 | "where $y[n] = y(n T_s)$, $x[n] = x(n T_s)$, and $h[n] = h(n T_s)$ denote the discrete-time versions of the involved entities.\n",
348 | "\n",
349 | "*Exercise:* Write a function called `naive_convolution()` that computes the convolution of two one-dimensional arrays (discrete-time signals of finite length) by means of two nested loops according to the equation above, where $x$ and $h$ are one-dimensional arrays of finite lengths. The infinite sum can be changed to a finite sum by assuming that all values before index 0 and all values after the last array element are equal to zero.\n",
350 | "\n",
351 | "Following this assumption, at which indices $n$ does $y[n]$ have its first and last non-zero value?"
352 | ]
353 | },
354 | {
355 | "cell_type": "code",
356 | "execution_count": 9,
357 | "metadata": {
358 | "execution": {
359 | "iopub.execute_input": "2020-11-26T13:08:54.308282Z",
360 | "iopub.status.busy": "2020-11-26T13:08:54.307748Z",
361 | "iopub.status.idle": "2020-11-26T13:08:54.424036Z",
362 | "shell.execute_reply": "2020-11-26T13:08:54.424503Z"
363 | }
364 | },
365 | "outputs": [
366 | {
367 | "data": {
368 | "text/plain": [
369 | "Text(0.5, 0, 'n')"
370 | ]
371 | },
372 | "execution_count": 1,
373 | "metadata": {},
374 | "output_type": "execute_result"
375 | },
376 | {
377 | "data": {
378 | "image/png": "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\n",
379 | "text/plain": [
380 | ""
381 | ]
382 | },
383 | "metadata": {
384 | "needs_background": "light"
385 | },
386 | "output_type": "display_data"
387 | }
388 | ],
389 | "source": [
390 | "def naive_convolution(x, h):\n",
391 | " # in python, you have to indent everything inside your function\n",
392 | "\n",
393 | " Nx = len(x) # length of x\n",
394 | " Nh = len(h) # length of h\n",
395 | " Ny = Nx + Nh - 1 # resulting length of y\n",
396 | "\n",
397 | " y = np.zeros(Ny) # initialise output array\n",
398 | " for k in np.arange(0, Nx): # help: for which indices k is x non-zero?\n",
399 | " for n in np.arange(k, Nh+k): # help: for which indices (n-k) is h non-zero?\n",
400 | " y[n] = y[n] + x[k] * h[n-k]\n",
401 | "\n",
402 | " return y\n",
403 | " # end of function\n",
404 | "\n",
405 | "\n",
406 | "# try out your function\n",
407 | "x = np.array([1, 1, 1, 1, 1])\n",
408 | "h = np.array([1, 1, 1])\n",
409 | "\n",
410 | "y = naive_convolution(x, h)\n",
411 | "plt.stem(y, use_line_collection=True, basefmt='C0:')\n",
412 | "plt.ylabel('y[n]')\n",
413 | "plt.xlabel('n');"
414 | ]
415 | },
416 | {
417 | "cell_type": "markdown",
418 | "metadata": {},
419 | "source": [
420 | "### The Transfer Function\n",
421 | "\n"
422 | ]
423 | },
424 | {
425 | "cell_type": "markdown",
426 | "metadata": {},
427 | "source": [
428 | "The transfer function of an LTI-system is the temporal Fourier transform its impulse response"
429 | ]
430 | },
431 | {
432 | "cell_type": "markdown",
433 | "metadata": {},
434 | "source": [
435 | "$$ H(\\omega) = \\int_{-\\infty}^{\\infty} h(t) e^{-j\\omega t} \\mathrm d t$$"
436 | ]
437 | },
438 | {
439 | "cell_type": "markdown",
440 | "metadata": {},
441 | "source": [
442 | "*Exercise*: $y(t)$, $x(t)$ and $h(t)$ are related to each other by the convolution. How are the respective Fourier spectra $Y(\\omega)$, $X(\\omega)$, $H(\\omega)$ related?"
443 | ]
444 | },
445 | {
446 | "cell_type": "markdown",
447 | "metadata": {},
448 | "source": [
449 | " "
450 | ]
451 | },
452 | {
453 | "cell_type": "markdown",
454 | "metadata": {},
455 | "source": [
456 | "### A naive implementation of the Fourier Transform\n",
457 | "\n"
458 | ]
459 | },
460 | {
461 | "cell_type": "markdown",
462 | "metadata": {},
463 | "source": [
464 | "Time-continuous signals cannot be handled by computers. They are sampled in time with the sample period $T_s$. In order to compute the Fourier transform of a signal, one has to discretize the integral\n",
465 | "\n",
466 | "$$ H(\\omega) = \\sum_{n=-\\infty}^{\\infty} h[n] e^{-j\\omega n T_s} $$\n",
467 | "\n",
468 | "where $h[n] = h(n T_s)$ denotes the discrete version of the involved $h(t)$. This is also known as DTFT, discrete-time Fourier transform.\n",
469 | "\n",
470 | "*Exercise:* Write a function called `naive_ft()` that computes the Fourier transform of a given signal $x$ of finite length for different frequencies. Use two nested loops according for this, again. The infinite sum can be changed to a finite sum by assuming that all values before index 0 and all values after the last array element are equal to zero."
471 | ]
472 | },
473 | {
474 | "cell_type": "code",
475 | "execution_count": 10,
476 | "metadata": {
477 | "execution": {
478 | "iopub.execute_input": "2020-11-26T13:08:54.429356Z",
479 | "iopub.status.busy": "2020-11-26T13:08:54.428861Z",
480 | "iopub.status.idle": "2020-11-26T13:08:54.431154Z",
481 | "shell.execute_reply": "2020-11-26T13:08:54.430662Z"
482 | }
483 | },
484 | "outputs": [],
485 | "source": [
486 | "def naive_ft(x, f, fs):\n",
487 | " # inputs:\n",
488 | " # x - signal vector\n",
489 | " # f - time-frequencies\n",
490 | " # fs - sample rate\n",
491 | "\n",
492 | " # outputs:\n",
493 | " # X - frequency spectrum\n",
494 | "\n",
495 | " Nsig = len(x) # length of signal\n",
496 | " Nspec = len(f) # length of spectrum\n",
497 | "\n",
498 | " omega = 2*np.pi*f # angular frequency\n",
499 | "\n",
500 | " X = np.zeros(Nspec, dtype=complex) # initialise output spectrum\n",
501 | " for k in np.arange(0, Nspec): # angular frequency\n",
502 | " for n in np.arange(0, Nsig): # help: for which indices (n-k) is h non-zero?\n",
503 | " X[k] = X[k] + x[n]*np.exp(-1j*omega[k]*n/fs)\n",
504 | "\n",
505 | " return X\n",
506 | " # end of function"
507 | ]
508 | },
509 | {
510 | "cell_type": "markdown",
511 | "metadata": {},
512 | "source": [
513 | "*Exercise:* Compute the transfer function of the blackbox system `tools.blackbox`."
514 | ]
515 | },
516 | {
517 | "cell_type": "code",
518 | "execution_count": 11,
519 | "metadata": {
520 | "execution": {
521 | "iopub.execute_input": "2020-11-26T13:08:54.436783Z",
522 | "iopub.status.busy": "2020-11-26T13:08:54.436143Z",
523 | "iopub.status.idle": "2020-11-26T13:08:55.498636Z",
524 | "shell.execute_reply": "2020-11-26T13:08:55.498221Z"
525 | }
526 | },
527 | "outputs": [
528 | {
529 | "data": {
530 | "image/png": "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\n",
531 | "text/plain": [
532 | ""
533 | ]
534 | },
535 | "metadata": {
536 | "needs_background": "light"
537 | },
538 | "output_type": "display_data"
539 | }
540 | ],
541 | "source": [
542 | "fs = 44100 # sample rate\n",
543 | "# frequency axis\n",
544 | "f = np.logspace(-4, 0, 100)*fs/2\n",
545 | "# dirac as input signal\n",
546 | "x = np.zeros(2**10)\n",
547 | "x[0] = 1\n",
548 | "\n",
549 | "h = tools.blackbox(x, fs) # impulse response of black system\n",
550 | "H = naive_ft(h, f, fs) # transfer function of blackboard system\n",
551 | "\n",
552 | "plt.semilogx(f, 20*np.log10(abs(H)))\n",
553 | "plt.ylim([-60, 5])\n",
554 | "plt.xlabel('f / Hz')\n",
555 | "plt.ylabel('20 log10 |H| / dB')\n",
556 | "plt.title('DTFT of the blackbox system''s impulse response = Frequency Response')\n",
557 | "plt.grid(True)"
558 | ]
559 | },
560 | {
561 | "cell_type": "markdown",
562 | "metadata": {},
563 | "source": [
564 | "## Solutions\n",
565 | "\n",
566 | "If you had problems solving some of the exercises, don't despair!\n",
567 | "Have a look at the [example solutions](linear_systems-solutions.ipynb)."
568 | ]
569 | },
570 | {
571 | "cell_type": "markdown",
572 | "metadata": {},
573 | "source": [
574 | "
\n",
575 | " \n",
577 | " \n",
578 | " \n",
579 | " \n",
580 | " To the extent possible under law,\n",
581 | " the person who associated CC0\n",
582 | " with this work has waived all copyright and related or neighboring\n",
583 | " rights to this work.\n",
584 | "
"
585 | ]
586 | }
587 | ],
588 | "metadata": {
589 | "kernelspec": {
590 | "display_name": "mystiasp",
591 | "language": "python",
592 | "name": "mystiasp"
593 | },
594 | "language_info": {
595 | "codemirror_mode": {
596 | "name": "ipython",
597 | "version": 3
598 | },
599 | "file_extension": ".py",
600 | "mimetype": "text/x-python",
601 | "name": "python",
602 | "nbconvert_exporter": "python",
603 | "pygments_lexer": "ipython3",
604 | "version": "3.7.8"
605 | }
606 | },
607 | "nbformat": 4,
608 | "nbformat_minor": 1
609 | }
610 |
--------------------------------------------------------------------------------
/linear_systems_I.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Linear Systems I\n",
8 | "\n",
9 | "[return to main page](index.ipynb)\n",
10 | "\n",
11 | "## Preparations"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": 1,
17 | "metadata": {
18 | "execution": {
19 | "iopub.execute_input": "2020-11-26T13:08:56.568343Z",
20 | "iopub.status.busy": "2020-11-26T13:08:56.567903Z",
21 | "iopub.status.idle": "2020-11-26T13:08:56.926319Z",
22 | "shell.execute_reply": "2020-11-26T13:08:56.926742Z"
23 | }
24 | },
25 | "outputs": [],
26 | "source": [
27 | "import tools\n",
28 | "import sounddevice as sd # for playback\n",
29 | "import soundfile as sf # for reading a soundfile"
30 | ]
31 | },
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {},
35 | "source": [
36 | "And some other stuff:"
37 | ]
38 | },
39 | {
40 | "cell_type": "code",
41 | "execution_count": 2,
42 | "metadata": {
43 | "execution": {
44 | "iopub.execute_input": "2020-11-26T13:08:56.929940Z",
45 | "iopub.status.busy": "2020-11-26T13:08:56.929507Z",
46 | "iopub.status.idle": "2020-11-26T13:08:57.076082Z",
47 | "shell.execute_reply": "2020-11-26T13:08:57.076493Z"
48 | }
49 | },
50 | "outputs": [],
51 | "source": [
52 | "# remove \"inline\" to get a separate plotting window:\n",
53 | "import numpy as np\n",
54 | "import matplotlib.pyplot as plt\n",
55 | "%matplotlib inline"
56 | ]
57 | },
58 | {
59 | "cell_type": "markdown",
60 | "metadata": {},
61 | "source": [
62 | "## One-dimensional time-continuous Systems\n",
63 | "\n",
64 | "First, we will have a brief review on linear systems in one dimension. In our case, we will use time signals depending on $t \\in \\mathbb{R}$. Generally, the input signal $x(t) \\in \\mathbb{C}$ and the corresponding output signal $y(t) \\in \\mathbb{C}$ of a system $\\mathcal H$ are related via:\n",
65 | "\n",
66 | "$$y(t) = \\mathcal{H}\\{x(t)\\}\\,.$$\n",
67 | "\n",
68 | "### Linear Time-Invariant (LTI) Systems\n",
69 | "\n",
70 | "As simple as it sounds, LTI-system are linear and time-invariant\n",
71 | "\n",
72 | "#### Linearity\n",
73 | "\n",
74 | "*Exercise*: Explain the term \"linear\" in your own words. \n",
75 | " \n",
76 | "*Exercise*: What does this mean mathematically?\n",
77 | "\n",
78 | "$$\\mathcal{H}\\{A \\cdot x_1(t) + B \\cdot x_2(t)\\} = \\,???\\, \\text{ for all } A,B \\in \\mathbb{C}$$\n"
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "metadata": {},
84 | "source": [
85 | "#### Time-Invariance\n",
86 | "\n",
87 | "*Exercise*: Explain the term \"time-invariance\" in your own words.\n",
88 | "\n",
89 | "*Exercise*: What does this mean mathematically?\n",
90 | "\n",
91 | "$$\\mathcal{H}\\{x(t-\\tau)\\} = \\,???\\, \\text{ for all } \\tau \\in \\mathbb{R}$$\n",
92 | "\n",
93 | "if $y(t) = \\mathcal{H} \\{ x(t) \\}$ is known.\n",
94 | "\n",
95 | "#### Are these systems LTI?\n",
96 | "\n",
97 | "*Exercise*: Vote for your LTI-system.\n",
98 | "\n",
99 | "1. $\\displaystyle y(t) = a \\cdot x(t) $ with $a \\in \\mathbb{C}$ \n",
100 | " Yes: \n",
101 | " No: \n",
102 | " Result:\n",
103 | "2. $\\displaystyle y(t) = a \\cdot x(t) + b $ with $a,b \\in \\mathbb{C}$ \n",
104 | " Yes: \n",
105 | " No: \n",
106 | " Result:\n",
107 | "3. $\\displaystyle y(t) = a \\cdot x(t-t_0) $ with $a \\in \\mathbb{C}$ and $t_0 \\in \\mathbb{R}$ \n",
108 | " Yes: \n",
109 | " No: \n",
110 | " Result:\n",
111 | "4. $\\displaystyle y(t) = a \\cdot x(t-b \\cdot t) $ with $a \\in \\mathbb{C}$ and $b \\in \\mathbb{R}$ \n",
112 | " Yes: \n",
113 | " No: \n",
114 | " Result: \n",
115 | "5. $\\displaystyle y(t) = \\frac{\\mathrm d x(t)}{\\mathrm d t}$ \n",
116 | " Yes: \n",
117 | " No: \n",
118 | " Result: \n",
119 | "6. $\\displaystyle y(t) = \\int x(t)\\,\\mathrm d t $ \n",
120 | " Yes: \n",
121 | " No: \n",
122 | " Result: \n",
123 | "7. $\\displaystyle y(t) = \\int_{-\\infty}^{\\infty} h(t_0) \\cdot x(t - t_0)\\,\\mathrm d t_0 $ \n",
124 | " Yes: \n",
125 | " No: \n",
126 | " Result:\n",
127 | " \n",
128 | "#### Listen to a linear and a non-linear system\n",
129 | "\n",
130 | "We will investigate two unknown systems. The only information we have about these systems is that the first is LTI (linear and time invariant) and the second is non-linear. They are defined by the functions `tools.blackbox()` and `tools.blackbox_nonlinear()`. Have a quick look at the documentation:"
131 | ]
132 | },
133 | {
134 | "cell_type": "code",
135 | "execution_count": 3,
136 | "metadata": {
137 | "execution": {
138 | "iopub.execute_input": "2020-11-26T13:08:57.085073Z",
139 | "iopub.status.busy": "2020-11-26T13:08:57.084652Z",
140 | "iopub.status.idle": "2020-11-26T13:08:57.118408Z",
141 | "shell.execute_reply": "2020-11-26T13:08:57.118823Z"
142 | }
143 | },
144 | "outputs": [],
145 | "source": [
146 | "tools.blackbox?"
147 | ]
148 | },
149 | {
150 | "cell_type": "code",
151 | "execution_count": 4,
152 | "metadata": {
153 | "execution": {
154 | "iopub.execute_input": "2020-11-26T13:08:57.121626Z",
155 | "iopub.status.busy": "2020-11-26T13:08:57.121211Z",
156 | "iopub.status.idle": "2020-11-26T13:08:57.124859Z",
157 | "shell.execute_reply": "2020-11-26T13:08:57.124440Z"
158 | }
159 | },
160 | "outputs": [],
161 | "source": [
162 | "tools.blackbox_nonlinear?"
163 | ]
164 | },
165 | {
166 | "cell_type": "markdown",
167 | "metadata": {},
168 | "source": [
169 | "*Exercise:* Load the audio file [data/xmas.wav](data/xmas.wav) and apply both functions to it."
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": 5,
175 | "metadata": {
176 | "execution": {
177 | "iopub.execute_input": "2020-11-26T13:08:57.129617Z",
178 | "iopub.status.busy": "2020-11-26T13:08:57.129214Z",
179 | "iopub.status.idle": "2020-11-26T13:08:57.130890Z",
180 | "shell.execute_reply": "2020-11-26T13:08:57.131358Z"
181 | }
182 | },
183 | "outputs": [],
184 | "source": [
185 | "# how to read an audio file\n",
186 | "sf.read?"
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": null,
192 | "metadata": {},
193 | "outputs": [],
194 | "source": []
195 | },
196 | {
197 | "cell_type": "markdown",
198 | "metadata": {},
199 | "source": [
200 | "*Exercise*: Listen to the input signal and both output signals."
201 | ]
202 | },
203 | {
204 | "cell_type": "code",
205 | "execution_count": 6,
206 | "metadata": {
207 | "execution": {
208 | "iopub.execute_input": "2020-11-26T13:08:57.135016Z",
209 | "iopub.status.busy": "2020-11-26T13:08:57.133682Z",
210 | "iopub.status.idle": "2020-11-26T13:08:57.137317Z",
211 | "shell.execute_reply": "2020-11-26T13:08:57.137749Z"
212 | }
213 | },
214 | "outputs": [],
215 | "source": [
216 | "# how to play back the signal\n",
217 | "sd.play?"
218 | ]
219 | },
220 | {
221 | "cell_type": "code",
222 | "execution_count": null,
223 | "metadata": {},
224 | "outputs": [],
225 | "source": []
226 | },
227 | {
228 | "cell_type": "markdown",
229 | "metadata": {},
230 | "source": [
231 | "### The Impulse Response\n",
232 | "\n",
233 | "The impulse response $h(t)$ of an LTI system characterises it completely. It is the LTI system's response to a Dirac impulse $\\delta(t)$\n",
234 | "\n",
235 | "$$h(t) = \\mathcal{H}\\{\\delta(t)\\}\\,.$$\n",
236 | "\n",
237 | "So why is the impulse response sufficient to describe the whole LTI system? The input signal $x(t)$ can be described as a sequence of Dirac impulses \n",
238 | "\n",
239 | "$$ x(t) = \\int_{-\\infty}^{\\infty} x(t_0) \\cdot \\delta(t-t_0)\\,\\mathrm d t_0\\,,$$ \n",
240 | "\n",
241 | "where the Dirac impulse at $t_0$ is weighted by the value $x(t_0)$ of the signal at $t_0$. Applying the system onto $x(t)$ yields\n",
242 | "\n",
243 | "$$ y(t) = \\mathcal{H}\\{x(t)\\} = \\mathcal{H}\\left\\{\\int_{-\\infty}^{\\infty} x(t_0) \\cdot \\delta(t-t_0)\\,\\mathrm d t_0\\right\\}$$\n",
244 | "\n",
245 | "As a next step, we can exchange the integral operator $\\int$ and the system operator $\\mathcal{H}$:\n",
246 | "\n",
247 | "$$ y(t) = \\mathcal{H}\\{x(t)\\} = \\int_{-\\infty}^{\\infty} x(t_0) \\cdot \\mathcal{H}\\{\\delta(t-t_0)\\}\\,\\mathrm d t_0$$\n",
248 | "\n",
249 | "*Exercise*: What property has to be fulfilled by $\\mathcal{H}$ in order to be able to exchange integral and system operator?"
250 | ]
251 | },
252 | {
253 | "cell_type": "code",
254 | "execution_count": 7,
255 | "metadata": {
256 | "execution": {
257 | "iopub.execute_input": "2020-11-26T13:08:57.140274Z",
258 | "iopub.status.busy": "2020-11-26T13:08:57.139890Z",
259 | "iopub.status.idle": "2020-11-26T13:08:57.142030Z",
260 | "shell.execute_reply": "2020-11-26T13:08:57.141626Z"
261 | }
262 | },
263 | "outputs": [],
264 | "source": [
265 | "# here no code, but explanation"
266 | ]
267 | },
268 | {
269 | "cell_type": "markdown",
270 | "metadata": {},
271 | "source": [
272 | "As the last step, we re-write the system response of the Dirac $\\delta$ shifted about $t_0$ as the shifted impulse response $h(t-t_0)$:\n",
273 | "\n",
274 | "$$ y(t) = \\mathcal{H}\\{x(t)\\} = \\int_{-\\infty}^{\\infty} x(t_0) \\cdot h(t-t_0)\\,\\mathrm d t_0$$\n",
275 | "\n",
276 | "*Exercise*: What property has to be fulfilled by $\\mathcal{H}$ in order to replace $\\mathcal{H}\\{\\delta(t-t_0)\\}$ by $h(t-t_0)$?"
277 | ]
278 | },
279 | {
280 | "cell_type": "code",
281 | "execution_count": 8,
282 | "metadata": {
283 | "execution": {
284 | "iopub.execute_input": "2020-11-26T13:08:57.144454Z",
285 | "iopub.status.busy": "2020-11-26T13:08:57.144062Z",
286 | "iopub.status.idle": "2020-11-26T13:08:57.146045Z",
287 | "shell.execute_reply": "2020-11-26T13:08:57.145630Z"
288 | }
289 | },
290 | "outputs": [],
291 | "source": [
292 | "# here no code, but explanation "
293 | ]
294 | },
295 | {
296 | "cell_type": "markdown",
297 | "metadata": {},
298 | "source": [
299 | "Hence, we can describe the output signal $y(t)$ by the so-called **linear convolution** integral of the corresponding input signal $x(t)$ and the impulse response $h(t)$. Its short version reads\n",
300 | "\n",
301 | "$$ y(t) = x(t) * h(t) $$\n",
302 | "\n",
303 | "where $*$ is a common notation of the convolution."
304 | ]
305 | },
306 | {
307 | "cell_type": "markdown",
308 | "metadata": {},
309 | "source": [
310 | "### A Naive Implementation of the Linear Convolution\n",
311 | "\n",
312 | "Time-continuous signals cannot be handled by computers. Signals must be sampled in time with the sample period $T_s$.\n",
313 | "This yields discrete-time signals. The discrete-time counterpart of a linear convolution is given as\n",
314 | "\n",
315 | "$$ y[n] = x[n] \\ast h[n] = \\sum_{k = -\\infty}^{\\infty} x[k] \\cdot h[n-k] $$\n",
316 | "\n",
317 | "where $y[n] = y(n T_s)$, $x[n] = x(n T_s)$, and $h[n] = h(n T_s)$ denote the discrete-time versions of the involved entities.\n",
318 | "\n",
319 | "*Exercise:* Write a function called `naive_convolution()` that computes the convolution of two one-dimensional arrays (discrete-time signals of finite length) by means of two nested loops according to the equation above, where $x$ and $h$ are one-dimensional arrays of finite lengths. The infinite sum can be changed to a finite sum by assuming that all values before index 0 and all values after the last array element are equal to zero.\n",
320 | "\n",
321 | "Following this assumption, at which indices $n$ does $y[n]$ have its first and last non-zero value?"
322 | ]
323 | },
324 | {
325 | "cell_type": "code",
326 | "execution_count": 9,
327 | "metadata": {
328 | "execution": {
329 | "iopub.execute_input": "2020-11-26T13:08:57.149654Z",
330 | "iopub.status.busy": "2020-11-26T13:08:57.149220Z",
331 | "iopub.status.idle": "2020-11-26T13:08:57.151295Z",
332 | "shell.execute_reply": "2020-11-26T13:08:57.150879Z"
333 | }
334 | },
335 | "outputs": [],
336 | "source": [
337 | "def naive_convolution(x, h):\n",
338 | " # in python, you have to indent everything inside your function\n",
339 | "\n",
340 | " # Nx = ??? # length of x\n",
341 | " # Nh = ??? # length of h\n",
342 | " # Ny = ??? # resulting length of y\n",
343 | "\n",
344 | " y = np.zeros(Ny) # initialise output array\n",
345 | " # for k in np.arange(???,???): # help: for which indices k is x non-zero?\n",
346 | " # for n in np.arange(???,???): # help: for which indices (n-k) is h non-zero?\n",
347 | " # place the code for the loops here\n",
348 | "\n",
349 | " # end of loop\n",
350 | " # end of loop\n",
351 | " return y\n",
352 | " # end of function\n",
353 | "\n",
354 | "\n",
355 | "# try out your function\n",
356 | "x = np.array([1, 1, 1, 1, 1]) # x\n",
357 | "h = np.array([1, 1, 1]) # h\n",
358 | "\n",
359 | "# y = naive_convolution(x, h)\n",
360 | "# plt.stem(y, use_line_collection=True, basefmt='C0:');\n",
361 | "# do not forget correct labeling of the axes"
362 | ]
363 | },
364 | {
365 | "cell_type": "markdown",
366 | "metadata": {},
367 | "source": [
368 | "### The Transfer Function\n",
369 | "\n"
370 | ]
371 | },
372 | {
373 | "cell_type": "markdown",
374 | "metadata": {},
375 | "source": [
376 | "The transfer function of an LTI-system is the temporal Fourier transform its impulse response"
377 | ]
378 | },
379 | {
380 | "cell_type": "markdown",
381 | "metadata": {},
382 | "source": [
383 | "$$ H(\\omega) = \\int_{-\\infty}^{\\infty} h(t) e^{-j\\omega t} \\mathrm d t$$"
384 | ]
385 | },
386 | {
387 | "cell_type": "markdown",
388 | "metadata": {},
389 | "source": [
390 | "*Exercise*: $y(t)$, $x(t)$ and $h(t)$ are related to each other by the convolution. How are the respective Fourier spectra $Y(\\omega)$, $X(\\omega)$, $H(\\omega)$ related?"
391 | ]
392 | },
393 | {
394 | "cell_type": "markdown",
395 | "metadata": {},
396 | "source": [
397 | " "
398 | ]
399 | },
400 | {
401 | "cell_type": "markdown",
402 | "metadata": {},
403 | "source": [
404 | "### A naive implementation of the Fourier Transform\n",
405 | "\n"
406 | ]
407 | },
408 | {
409 | "cell_type": "markdown",
410 | "metadata": {},
411 | "source": [
412 | "Time-continuous signals can not be easily handled by todays' computers. They are sampled in time with the sample period $T_s$. In order to compute the Fourier transform of a signal, one has to discretize the integral\n",
413 | "\n",
414 | "$$ H(\\omega) = \\sum_{n=-\\infty}^{\\infty} h[n] e^{-j\\omega n T_s} $$\n",
415 | "\n",
416 | "where $h[n] = h(n T_s)$ denotes the discrete version of the involved $h(t)$. This is also known as DTFT, discrete-time Fourier transform.\n",
417 | "\n",
418 | "*Exercise:* Write a function called `naive_ft()` that computes the Fourier transform of a given signal $x$ of finite length for different frequencies, which are also given in an array. Use two nested loops according for this, again. The infinite sum can be changed to a finite sum by assuming that all values before index 0 and all values after the last array element are equal to zero."
419 | ]
420 | },
421 | {
422 | "cell_type": "code",
423 | "execution_count": 10,
424 | "metadata": {
425 | "execution": {
426 | "iopub.execute_input": "2020-11-26T13:08:57.154565Z",
427 | "iopub.status.busy": "2020-11-26T13:08:57.154159Z",
428 | "iopub.status.idle": "2020-11-26T13:08:57.155796Z",
429 | "shell.execute_reply": "2020-11-26T13:08:57.156183Z"
430 | }
431 | },
432 | "outputs": [],
433 | "source": [
434 | "def naive_ft(x, f, fs):\n",
435 | " # inputs:\n",
436 | " # x - signal vector\n",
437 | " # f - time-frequencies\n",
438 | " # fs - sample rate\n",
439 | " \n",
440 | " # outputs:\n",
441 | " # X - frequency spectrum\n",
442 | " \n",
443 | " # Nsig = ??? # length of signal\n",
444 | " # Nspec = ??? # length of spectrum\n",
445 | " \n",
446 | " # omega = 2*np.pi*f # angular frequency\n",
447 | " \n",
448 | " X = np.zeros(Nspec, dtype=complex) # initialise output spectrum\n",
449 | " # for k in np.arange(???, ???): # loop over angular frequencies\n",
450 | " # for n in np.arange(???, ???): # loop over \n",
451 | " # place the code for the loops here\n",
452 | " \n",
453 | " return X\n",
454 | " # end of function"
455 | ]
456 | },
457 | {
458 | "cell_type": "markdown",
459 | "metadata": {},
460 | "source": [
461 | "*Exercise:* Compute the transfer function of the blackbox system `tools.blackbox`."
462 | ]
463 | },
464 | {
465 | "cell_type": "code",
466 | "execution_count": 11,
467 | "metadata": {
468 | "execution": {
469 | "iopub.execute_input": "2020-11-26T13:08:57.159797Z",
470 | "iopub.status.busy": "2020-11-26T13:08:57.159362Z",
471 | "iopub.status.idle": "2020-11-26T13:08:57.162844Z",
472 | "shell.execute_reply": "2020-11-26T13:08:57.162418Z"
473 | }
474 | },
475 | "outputs": [],
476 | "source": [
477 | "fs = 44100; # sample rate\n",
478 | "f = np.logspace(-4, 0, 100)*fs/2; # frequency axis\n",
479 | "# dirac as input signal\n",
480 | "x = np.zeros(2**10)\n",
481 | "x[0] = 1;\n",
482 | "\n",
483 | "h = tools.blackbox(x, fs); # impulse response of black-box system\n",
484 | "# H = naive_ft(h, f, fs); # transfer function of blackboard system\n",
485 | "\n",
486 | "# plt.semilogx(f, 20*np.log10(abs(H)));\n",
487 | "# plt.ylim([-60, 5])\n",
488 | "# do not forget correct labeling of the axes"
489 | ]
490 | },
491 | {
492 | "cell_type": "markdown",
493 | "metadata": {},
494 | "source": [
495 | "## Solutions\n",
496 | "\n",
497 | "If you had problems solving some of the exercises, don't despair!\n",
498 | "Have a look at the [example solutions](linear_systems_I-solutions.ipynb)."
499 | ]
500 | },
501 | {
502 | "cell_type": "markdown",
503 | "metadata": {},
504 | "source": [
505 | "
\n",
506 | " \n",
508 | " \n",
509 | " \n",
510 | " \n",
511 | " To the extent possible under law,\n",
512 | " the person who associated CC0\n",
513 | " with this work has waived all copyright and related or neighboring\n",
514 | " rights to this work.\n",
515 | "
"
516 | ]
517 | }
518 | ],
519 | "metadata": {
520 | "kernelspec": {
521 | "display_name": "mystiasp",
522 | "language": "python",
523 | "name": "mystiasp"
524 | },
525 | "language_info": {
526 | "codemirror_mode": {
527 | "name": "ipython",
528 | "version": 3
529 | },
530 | "file_extension": ".py",
531 | "mimetype": "text/x-python",
532 | "name": "python",
533 | "nbconvert_exporter": "python",
534 | "pygments_lexer": "ipython3",
535 | "version": "3.7.8"
536 | }
537 | },
538 | "nbformat": 4,
539 | "nbformat_minor": 1
540 | }
541 |
--------------------------------------------------------------------------------
/linear_systems_II-solutions.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Linear Systems II\n",
8 | "\n",
9 | "[return to main page](index.ipynb)\n",
10 | "\n",
11 | "## Preparations"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": 1,
17 | "metadata": {
18 | "execution": {
19 | "iopub.execute_input": "2020-11-26T13:10:20.490206Z",
20 | "iopub.status.busy": "2020-11-26T13:10:20.489800Z",
21 | "iopub.status.idle": "2020-11-26T13:10:20.868958Z",
22 | "shell.execute_reply": "2020-11-26T13:10:20.869361Z"
23 | }
24 | },
25 | "outputs": [
26 | {
27 | "data": {
28 | "text/plain": [
29 | "> 0 Built-in Microphone, Core Audio (2 in, 0 out)\n",
30 | "< 1 Built-in Output, Core Audio (0 in, 2 out)\n",
31 | " 2 DisplayPort, Core Audio (0 in, 2 out)\n",
32 | " 3 Aggregate Device, Core Audio (0 in, 2 out)"
33 | ]
34 | },
35 | "execution_count": 1,
36 | "metadata": {},
37 | "output_type": "execute_result"
38 | }
39 | ],
40 | "source": [
41 | "import tools\n",
42 | "import numpy as np\n",
43 | "import sounddevice as sd # for playback\n",
44 | "import soundfile as sf # for reading a soundfile\n",
45 | "from scipy import signal\n",
46 | "sd.query_devices() # check if desired default device is set"
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {},
52 | "source": [
53 | "## Multi-dimensional continuous Systems\n",
54 | "\n",
55 | "In the first part of this exercise, we had a quick review on linear systems in one dimension, namely the time $t$. Now, we add three additional dimension $\\mathbf x = [x,y,z]^T$ in order to cover the three-dimensional space. Generally, the input signal $q \\in \\mathbb{C}$ and the corresponding output signal $p \\in \\mathbb{C}$ of a system $\\mathcal H$ are related via:\n",
56 | "\n",
57 | "$$p(\\mathbf x, t) = p(x,y,z,t) = \\mathcal{H}\\{q(\\mathbf x,t)\\} = \\mathcal{H}\\{q(x,y,z,t)\\}\\,.$$"
58 | ]
59 | },
60 | {
61 | "cell_type": "markdown",
62 | "metadata": {},
63 | "source": [
64 | "### Linear Time-Space-Invariant (LTSI) Systems\n",
65 | "\n",
66 | "As simple as it sounds, LTI-system are linear, time-invariant and **space-invariant**. We had already covered the first two properties in the [last exercise](linear_systems_I.ipynb#Linear-Time-Invariant-%28LTI%29-Systems)."
67 | ]
68 | },
69 | {
70 | "cell_type": "markdown",
71 | "metadata": {},
72 | "source": [
73 | "#### Space-Invariance\n",
74 | "\n",
75 | "*Exercise*: Explain the term \"space-invariance\" in your own words.\n",
76 | "\n",
77 | "*\n",
78 | "The properties of the system to not change over space: If a distinct input signal causes corresponding system response, a shifted version of that input signal results a shifted version of the system responses. \n",
79 | "*\n",
80 | "\n",
81 | "\n",
82 | "*Exercise*: What does this mean mathematically?\n",
83 | "\n",
84 | "\n",
85 | "$$\\mathcal{H}\\{q(\\mathbf x - \\mathbf x_0, t)\\} = \\,p(\\mathbf x - \\mathbf x_0, t)\\, \\text{ for all } \\mathbf x_0 \\in \\mathbb{R}$$\n",
86 | "\n",
87 | "\n",
88 | "if $p(\\mathbf x, t) = \\mathcal{H} \\{ q(\\mathbf x,t) \\}$ is known.\n",
89 | "\n",
90 | "*Exercise*: Can you give an example, for which environment the space invariance does **not** hold?\n",
91 | "\n",
92 | "\n",
93 | "*\n",
94 | "Any ordinary room implying boundary conditions.\n",
95 | "* \n",
96 | "\n"
97 | ]
98 | },
99 | {
100 | "cell_type": "markdown",
101 | "metadata": {},
102 | "source": [
103 | "### The Impulse Response\n",
104 | "\n",
105 | "#### ... of an LTSI System\n",
106 | "\n",
107 | "The impulse response $h(\\mathbf x, t)$ of an LTSI system characterises it completely. It is the system's response to a spatio-temporal Dirac impulse $\\delta(\\mathbf x)\\delta(t)$\n",
108 | "\n",
109 | "$$h(\\mathbf x, t) = h(x, y, z, t) = \\mathcal{H}\\{\\delta(\\mathbf x)\\delta(t)\\} = \\mathcal{H}\\{\\delta(x)\\delta(y)\\delta(z)\\delta(t)\\}\\,.$$\n",
110 | "\n",
111 | "We can describe the output signal $p(\\mathbf x, t)$ by a 4-dimensional convolution of the corresponding input signal $q(\\mathbf x, t)$ and the impulse response $h(\\mathbf x, t)$: \n",
112 | "\n",
113 | "$$ p(\\mathbf x, t) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} q(x_0, y_0, z_0, t_0)\\,h(x - x_0, y - y_0, z - z_0, t - t_0)\\,\\mathrm d x_0 \\mathrm d y_0 \\mathrm d z_0 \\mathrm d t_0$$\n",
114 | "\n",
115 | "Its short version reads\n",
116 | "\n",
117 | "$$ p(\\mathbf x, t) = q(\\mathbf x, t) *_{\\mathbf x, t} h(\\mathbf x, t) $$\n",
118 | "\n",
119 | "where $*$ is a common notation of the convolution.\n",
120 | "\n",
121 | "#### ... of an shift-variant LTI System\n",
122 | "\n",
123 | "For a shift-variant system, the impulse response is not independent from the shift in space:\n",
124 | "\n",
125 | "$$h(\\mathbf x, \\mathbf x_0, t) = \\mathcal{H}\\{\\delta(\\mathbf x - \\mathbf x_0)\\delta(t)\\}$$\n",
126 | "\n",
127 | "We can no longer describe the resulting output signal as a convolution with respect to the three dimensions in space:\n",
128 | "\n",
129 | "$$ p(\\mathbf x, t) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} q(x_0, y_0, z_0, t_0)\\,h(x,x_0,y,y_0,z,z_0,t-t_0)\\,\\mathrm d x_0\\,\\mathrm d y_0\\,\\mathrm d z_0\\,\\mathrm d t_0$$\n",
130 | "\n",
131 | "The integral with respect to time is still a linear convolution, though.\n"
132 | ]
133 | },
134 | {
135 | "cell_type": "markdown",
136 | "metadata": {},
137 | "source": [
138 | "### The Spatial Frequency Response\n",
139 | "\n",
140 | "For an LTSI-System, the spatial transfer characteristics in terms of its frequency response is defined as the three-dimensional Fourier transform with respect to each dimension in space:\n",
141 | "\n",
142 | "$$ \\tilde h(\\mathbf k, t) = \\tilde h(k_x, k_y, k_z, t) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} h(x,y,z,t) e^{+jk_x x} e^{+jk_y y} e^{+jk_z z} \\,\\mathrm d x\\,\\mathrm d y\\,\\mathrm d z$$\n",
143 | "\n",
144 | "with the wave vector $\\mathbf k = [k_x, k_y, k_z]^T$. The Fourier transform with respect to one dimension is equivalent to the temporal Fourier transform with e.g. $\\omega \\rightarrow -k_x$.\n",
145 | "\n",
146 | "### The Spatio-Temporal Frequency Response\n",
147 | "\n",
148 | "Applying the temporal Fourier transform to $\\tilde h(\\mathbf k, t)$ yields the **Spatio-Temporal Transfer Function** of an LTSI-System\n",
149 | "\n",
150 | "$$ \\tilde H(\\mathbf k, \\omega) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} h(x,y,z,t) e^{+jk_x x} e^{+jk_y y} e^{+jk_z z} e^{-j\\omega t} \\,\\mathrm d x\\,\\mathrm d y\\,\\mathrm d z\\,\\mathrm d t$$\n",
151 | "\n",
152 | "## Sound Propagation as a Linear System\n",
153 | "\n",
154 | "Remember the inhomogeneous wave equation?\n",
155 | "\n",
156 | "$$\\Delta p(\\mathbf x, t) - \\frac{1}{c^2} \\frac{\\partial^2}{\\partial t^2} p(\\mathbf x, t) = -q(\\mathbf x, t)$$\n",
157 | "\n",
158 | "Its solution $p(\\mathbf x, t)$ has to incorporate boundary conditions and the inhomogenity $q(\\mathbf x, t)$. It describes sound propagation and can be interpreted as an LTI-system with the source density $q(\\mathbf x, t)$ as the input signal and the sound pressure $p(\\mathbf x, t)$ as the output signal. Remember the Green's function? It corresponds to the special inhomogenity $q(\\mathbf x, t) = \\delta(\\mathbf x - \\mathbf x_0)\\delta(t-t_0)$ and fulfils the boundary conditions. The inhomogeneous wave equation for the Green's functions reads\n",
159 | "\n",
160 | "$$\\Delta g(\\mathbf x | \\mathbf x_0 , t) - \\frac{1}{c^2} \\frac{\\partial^2}{\\partial t^2} g(\\mathbf x | \\mathbf x_0 , t) = - \\delta(\\mathbf x - \\mathbf x_0)\\delta(t)\\,.$$\n",
161 | "\n",
162 | "*Exercise*: What is the impulse response $h(\\mathbf x,\\mathbf x_0,t)$ of the wave equation as a linear system?\n",
163 | "\n",
164 | "\n",
165 | "*\n",
166 | "The Green's function represents the impulse response of the wave equation.\n",
167 | "*\n",
168 | "\n",
169 | "\n",
170 | "Under free-field conditions, i.e. no boundary conditions, the wave equation becomes an LTSI-System with free-field Green's function $g_0(\\mathbf x , t) = \\delta(t - |\\mathbf x|/c)$ as its spatio-temporal impulse responses. In rooms, the impulse response is often subsumed under the term **Room Impulse Response**.\n",
171 | "\n",
172 | "### Measuring a Room Impulse Response\n",
173 | "\n",
174 | "#### A Very Simplistic Procedure\n",
175 | "\n",
176 | "There are several possibilities to excite the room with a signal being close to a Dirac - bursting balloons, gunshots, electrical spark discharges, ...; for simplicity (and for safety reasons), one could use two wooden boards which are clapped together. The signal recorded by a microphone standing at the certain positions $\\mathbf x$ then yields the approximate room impulse response $h(\\mathbf x,\\mathbf x_0,t)$\n",
177 | "\n",
178 | "\n",
179 | "\n",
180 | "That's not ideal - ideally all frequencies should be excited equally - but it shall be sufficient for demonstration purposes. As we will get to know later, that there are *much better* ways to measure room impulse responses!"
181 | ]
182 | },
183 | {
184 | "cell_type": "code",
185 | "execution_count": 2,
186 | "metadata": {
187 | "execution": {
188 | "iopub.execute_input": "2020-11-26T13:10:20.873308Z",
189 | "iopub.status.busy": "2020-11-26T13:10:20.872887Z",
190 | "iopub.status.idle": "2020-11-26T13:10:27.357020Z",
191 | "shell.execute_reply": "2020-11-26T13:10:27.356572Z"
192 | }
193 | },
194 | "outputs": [],
195 | "source": [
196 | "# speech signal\n",
197 | "speech, fs = sf.read(\"data/xmas.wav\")\n",
198 | "# sloppy measured room impulse response\n",
199 | "# (measured with clap seen above, do not harm your ears!)\n",
200 | "rir, fs_rir = sf.read(\"data/rir_clap.wav\")\n",
201 | "\n",
202 | "assert fs == fs_rir # compare sample rates of signal and rir\n",
203 | "speech_clap = signal.fftconvolve(speech, rir) # convolve with respect to time\n",
204 | "# normalize to the same maximum value as the original speech signal:\n",
205 | "speech_clap = tools.normalize(speech_clap, np.max(np.abs(speech)))\n",
206 | "\n",
207 | "sd.play(speech, fs, blocking=True)\n",
208 | "sd.play(speech_clap, fs, blocking=True)"
209 | ]
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "metadata": {},
214 | "source": [
215 | "#### More elaborated procedures\n",
216 | "\n",
217 | "In slightly more modern *sweep method*, room is excited with a sine sweep, which is reproduced by means of a loudspeaker. The actual impulse response will be calculated from the excitation signal and the signal recorded by the microphone. Let's listen to some sweep signals (**watch the volume!**):"
218 | ]
219 | },
220 | {
221 | "cell_type": "code",
222 | "execution_count": 3,
223 | "metadata": {
224 | "execution": {
225 | "iopub.execute_input": "2020-11-26T13:10:27.361040Z",
226 | "iopub.status.busy": "2020-11-26T13:10:27.360475Z",
227 | "iopub.status.idle": "2020-11-26T13:10:33.644624Z",
228 | "shell.execute_reply": "2020-11-26T13:10:33.644130Z"
229 | }
230 | },
231 | "outputs": [],
232 | "source": [
233 | "fstart = 65 # start with 65 Hertz, be nice to your onboard loudspeakers\n",
234 | "fstop = 15000 # stop a 15000 Hertz\n",
235 | "tlen = 3 # length of sweep signal\n",
236 | "t = np.arange(0, tlen*fs)/fs # samples\n",
237 | "\n",
238 | "sweep_lin = signal.chirp(t, fstart, tlen, fstop, method='linear')\n",
239 | "sweep_log = signal.chirp(t, fstart, tlen, fstop, method='logarithmic')\n",
240 | "\n",
241 | "sd.play(sweep_lin, fs, blocking=True)\n",
242 | "sd.play(sweep_log, fs, blocking=True)"
243 | ]
244 | },
245 | {
246 | "cell_type": "markdown",
247 | "metadata": {},
248 | "source": [
249 | "## Fourier Analysis of a Sound Field\n",
250 | "\n",
251 | "### Spatial-Temporal Fourier Transform of a Plane Wave\n",
252 | "\n",
253 | "We already got to know the temporal spectrum of a [plane wave](physics_of_sound_I-solutions.ipynb#Plane-Wave) with a direction of propagation $\\mathbf n_{\\mathrm {pw}}$ which reads\n",
254 | "\n",
255 | "$$P_{pw}(\\mathbf x, \\omega) = \\mathrm{exp}\\left(-j\\frac{\\omega}{c} \\mathbf n_{\\mathrm {pw}} \\cdot \\mathbf x \\right) = \\mathrm{exp}\\left(-j\\frac{\\omega}{c} (n_{\\mathrm {pw},x} \\cdot x + n_{\\mathrm {pw},y} \\cdot y + n_{\\mathrm {pw},z} \\cdot z) \\right) \\,. $$\n",
256 | "\n",
257 | "*Exercise*: Calculate the three-dimensional, spatial Fourier Transform $\\tilde P_{pw}(\\mathbf k, \\omega)$ of the Plane Wave. Use the integral identity:\n",
258 | "\n",
259 | "$$ \\int_{-\\infty}^{\\infty} e^{+j a b}\\,\\mathrm d b\n",
260 | " = \\int_{-\\infty}^{\\infty} \\mathrm{exp}\\left(+j a b\\right)\\,\\mathrm d b \n",
261 | " = 2\\pi \\delta(a) \n",
262 | "$$"
263 | ]
264 | },
265 | {
266 | "cell_type": "markdown",
267 | "metadata": {
268 | "collapsed": true
269 | },
270 | "source": [
271 | "\n",
272 | "*\n",
273 | "\n",
274 | "As we already have the temporal spectrum of the plane wave we only need to calculate the three integral we respect to the space dimensions:\n",
275 | "\n",
276 | "$$ \\tilde P_{pw}(\\mathbf k, \\omega) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} P_{pw}(\\mathbf x, \\omega) e^{+jk_x x} e^{+jk_y y} e^{+jk_z z} \\,\\mathrm d x\\,\\mathrm d y\\,\\mathrm d z\n",
277 | "$$\n",
278 | "\n",
279 | "Inserting the spectrum of the plane wave\n",
280 | "$$\n",
281 | "\\tilde P_{pw}(\\mathbf k, \\omega) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} e^{-j\\frac{\\omega}{c} (n_{\\mathrm {pw},x} \\cdot x + n_{\\mathrm {pw},y} \\cdot y + n_{\\mathrm {pw},z} \\cdot z)} e^{+jk_x x} e^{+jk_y y} e^{+jk_z z} \\,\\mathrm d x\\,\\mathrm d y\\,\\mathrm d z\n",
282 | "$$\n",
283 | "\n",
284 | "Splitting up the exponentials and re-arranging the integrals yields\n",
285 | "\n",
286 | "$$\n",
287 | "\\tilde P_{pw}(\\mathbf k, \\omega) = \n",
288 | " \\int_{-\\infty}^{\\infty} \\mathrm{exp}\\left(+j\\left[k_x-\\frac{\\omega}{c} n_{\\mathrm {pw},x}\\right]x\\right) \\,\\mathrm d x \\cdot\n",
289 | " \\int_{-\\infty}^{\\infty} \\mathrm{exp}\\left(+j\\left[k_y-\\frac{\\omega}{c} n_{\\mathrm {pw},y}\\right]y\\right) \\,\\mathrm d y \\cdot\n",
290 | " \\int_{-\\infty}^{\\infty} \\mathrm{exp}\\left(+j\\left[k_z-\\frac{\\omega}{c} n_{\\mathrm {pw},z}\\right]z\\right) \\,\\mathrm d z\n",
291 | "$$\n",
292 | "\n",
293 | "Using the integral indentity for each of the integrals yields\n",
294 | "\n",
295 | "$$\n",
296 | "\\tilde P_{pw}(\\mathbf k, \\omega) = \n",
297 | " 2\\pi \\delta\\left(k_x-\\frac{\\omega}{c} n_{\\mathrm {pw},x}\\right) \\cdot\n",
298 | " 2\\pi \\delta\\left(k_y-\\frac{\\omega}{c} n_{\\mathrm {pw},y}\\right) \\cdot\n",
299 | " 2\\pi \\delta\\left(k_z-\\frac{\\omega}{c} n_{\\mathrm {pw},z}\\right)\n",
300 | " =\n",
301 | " (2\\pi)^3 \\delta\\left(\\mathbf k -\\frac{\\omega}{c} \\mathbf n_{\\mathrm {pw}}\\right)\n",
302 | "$$\n",
303 | "\n",
304 | "*\n",
305 | "\n"
306 | ]
307 | },
308 | {
309 | "cell_type": "markdown",
310 | "metadata": {},
311 | "source": [
312 | "### Fourier Analysis using a Plane Wave Decomposition\n",
313 | "\n",
314 | "We now know that the value of $\\tilde P(\\mathbf k, \\omega)$ at $\\mathbf k = \\frac{\\omega}{c} \\mathbf n_{\\mathrm {pw}}$ corresponds to the amplitude of the plane wave propagation in the directions of $\\mathbf n_{\\mathrm {pw}}$. This can be straightforwardly used to compute the so-called plane wave composition:\n",
315 | "\n",
316 | "$$\n",
317 | "\\bar P(\\mathbf n_{\\mathrm {pw}}) = \\tilde P(\\mathbf k, \\omega)|_{\\mathbf k = \\frac{\\omega}{c} \\mathbf n_{\\mathrm {pw}}}\n",
318 | "$$\n",
319 | "\n",
320 | "After all this math, u can lay back and have a look at the demonstration, how to compute a plane wave decompositon out of two microphone signals.\n",
321 | "\n"
322 | ]
323 | },
324 | {
325 | "cell_type": "markdown",
326 | "metadata": {},
327 | "source": [
328 | "
\n",
329 | " \n",
331 | " \n",
332 | " \n",
333 | " \n",
334 | " To the extent possible under law,\n",
335 | " the person who associated CC0\n",
336 | " with this work has waived all copyright and related or neighboring\n",
337 | " rights to this work.\n",
338 | "
"
339 | ]
340 | }
341 | ],
342 | "metadata": {
343 | "kernelspec": {
344 | "display_name": "mystiasp",
345 | "language": "python",
346 | "name": "mystiasp"
347 | },
348 | "language_info": {
349 | "codemirror_mode": {
350 | "name": "ipython",
351 | "version": 3
352 | },
353 | "file_extension": ".py",
354 | "mimetype": "text/x-python",
355 | "name": "python",
356 | "nbconvert_exporter": "python",
357 | "pygments_lexer": "ipython3",
358 | "version": "3.7.8"
359 | }
360 | },
361 | "nbformat": 4,
362 | "nbformat_minor": 1
363 | }
364 |
--------------------------------------------------------------------------------
/linear_systems_II.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Linear Systems II\n",
8 | "\n",
9 | "[return to main page](index.ipynb)\n",
10 | "\n",
11 | "## Preparations"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": 1,
17 | "metadata": {
18 | "execution": {
19 | "iopub.execute_input": "2020-11-26T13:10:06.061351Z",
20 | "iopub.status.busy": "2020-11-26T13:10:06.060911Z",
21 | "iopub.status.idle": "2020-11-26T13:10:06.491573Z",
22 | "shell.execute_reply": "2020-11-26T13:10:06.491116Z"
23 | }
24 | },
25 | "outputs": [
26 | {
27 | "data": {
28 | "text/plain": [
29 | "> 0 Built-in Microphone, Core Audio (2 in, 0 out)\n",
30 | "< 1 Built-in Output, Core Audio (0 in, 2 out)\n",
31 | " 2 DisplayPort, Core Audio (0 in, 2 out)\n",
32 | " 3 Aggregate Device, Core Audio (0 in, 2 out)"
33 | ]
34 | },
35 | "execution_count": 1,
36 | "metadata": {},
37 | "output_type": "execute_result"
38 | }
39 | ],
40 | "source": [
41 | "import tools\n",
42 | "import numpy as np\n",
43 | "import soundfile as sf # for reading a soundfile\n",
44 | "from scipy import signal\n",
45 | "\n",
46 | "# use the system OS sound device for convenience\n",
47 | "import sounddevice as sd # for playback\n",
48 | "# sd.default.device = 0 # set default device -> not used, know what you're doing\n",
49 | "sd.query_devices() # check if desired default device is set"
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "metadata": {},
55 | "source": [
56 | "## Multi-dimensional continuous Systems\n",
57 | "\n",
58 | "In the first part of this exercise, we had a quick review on linear systems in one dimension, namely the time $t$. Now, we add three additional dimension $\\mathbf x = [x,y,z]^T$ in order to cover the three-dimensional space. Generally, the input signal $q \\in \\mathbb{C}$ and the corresponding output signal $p \\in \\mathbb{C}$ of a system $\\mathcal H$ are related via:\n",
59 | "\n",
60 | "$$p(\\mathbf x, t) = p(x,y,z,t) = \\mathcal{H}\\{q(\\mathbf x,t)\\} = \\mathcal{H}\\{q(x,y,z,t)\\}\\,.$$"
61 | ]
62 | },
63 | {
64 | "cell_type": "markdown",
65 | "metadata": {},
66 | "source": [
67 | "### Linear Time-Space-Invariant (LTSI) Systems\n",
68 | "\n",
69 | "As simple as it sounds, LTI-system are linear, time-invariant and **space-invariant**. We had already covered the first two properties in the [last exercise](linear_systems_I.ipynb#Linear-Time-Invariant-%28LTI%29-Systems)."
70 | ]
71 | },
72 | {
73 | "cell_type": "markdown",
74 | "metadata": {},
75 | "source": [
76 | "#### Space-Invariance\n",
77 | "\n",
78 | "*Exercise*: Explain the term \"space-invariance\" in your own words.\n",
79 | "\n",
80 | "\n",
81 | "*Exercise*: What does this mean mathematically?\n",
82 | "\n",
83 | "$$\\mathcal{H}\\{q(\\mathbf x - \\mathbf x_0, t)\\} = \\,???\\, \\text{ for all } \\mathbf x_0 \\in \\mathbb{R}$$\n",
84 | "\n",
85 | "if $p(\\mathbf x, t) = \\mathcal{H} \\{ q(\\mathbf x,t) \\}$ is known.\n",
86 | "\n",
87 | "*Exercise*: Can you give an example, for which environment the space invariance does **not** hold?"
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "metadata": {},
93 | "source": [
94 | "### The Impulse Response\n",
95 | "\n",
96 | "#### ... of an LTSI System\n",
97 | "\n",
98 | "The impulse response $h(\\mathbf x, t)$ of an LTSI system characterises it completely. It is the system's response to a spatio-temporal Dirac impulse $\\delta(\\mathbf x)\\delta(t)$\n",
99 | "\n",
100 | "$$h(\\mathbf x, t) = h(x, y, z, t) = \\mathcal{H}\\{\\delta(\\mathbf x)\\delta(t)\\} = \\mathcal{H}\\{\\delta(x)\\delta(y)\\delta(z)\\delta(t)\\}\\,.$$\n",
101 | "\n",
102 | "We can describe the output signal $p(\\mathbf x, t)$ by a 4-dimensional convolution of the corresponding input signal $q(\\mathbf x, t)$ and the impulse response $h(\\mathbf x, t)$: \n",
103 | "\n",
104 | "$$ p(\\mathbf x, t) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} q(x_0, y_0, z_0, t_0)\\,h(x - x_0, y - y_0, z - z_0, t - t_0)\\,\\mathrm d x_0 \\mathrm d y_0 \\mathrm d z_0 \\mathrm d t_0$$\n",
105 | "\n",
106 | "Its short version reads\n",
107 | "\n",
108 | "$$ p(\\mathbf x, t) = q(\\mathbf x, t) *_{\\mathbf x, t} h(\\mathbf x, t) $$\n",
109 | "\n",
110 | "where $*$ is a common notation of the convolution.\n",
111 | "\n",
112 | "#### ... of an shift-variant LTI System\n",
113 | "\n",
114 | "For a shift-variant system, the impulse response is not independent from the shift in space:\n",
115 | "\n",
116 | "$$h(\\mathbf x, \\mathbf x_0, t) = \\mathcal{H}\\{\\delta(\\mathbf x - \\mathbf x_0)\\delta(t)\\}$$\n",
117 | "\n",
118 | "We can no longer describe the resulting output signal as a convolution with respect to the three dimensions in space:\n",
119 | "\n",
120 | "$$ p(\\mathbf x, t) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} q(x_0, y_0, z_0, t_0)\\,h(x,x_0,y,y_0,z,z_0,t-t_0)\\,\\mathrm d x_0\\,\\mathrm d y_0\\,\\mathrm d z_0\\,\\mathrm d t_0$$\n",
121 | "\n",
122 | "The integral with respect to time is still a linear convolution, though.\n"
123 | ]
124 | },
125 | {
126 | "cell_type": "markdown",
127 | "metadata": {},
128 | "source": [
129 | "### The Spatial Frequency Response\n",
130 | "\n",
131 | "For an LTSI-System, the spatial transfer characteristics in terms of its frequency response is defined as the three-dimensional Fourier transform with respect to each dimension in space:\n",
132 | "\n",
133 | "$$ \\tilde h(\\mathbf k, t) = \\tilde h(k_x, k_y, k_z, t) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} h(x,y,z,t) e^{+jk_x x} e^{+jk_y y} e^{+jk_z z} \\,\\mathrm d x\\,\\mathrm d y\\,\\mathrm d z$$\n",
134 | "\n",
135 | "with the wave vector $\\mathbf k = [k_x, k_y, k_z]^T$. The Fourier transform with respect to one dimension is equivalent to the temporal Fourier transform with e.g. $\\omega \\rightarrow -k_x$.\n",
136 | "\n",
137 | "### The Spatio-Temporal Frequency Response\n",
138 | "\n",
139 | "Applying the temporal Fourier transform to $\\tilde h(\\mathbf k, t)$ yields the **Spatio-Temporal Frequency Response** of an LTSI-System\n",
140 | "\n",
141 | "$$ \\tilde H(\\mathbf k, \\omega) = \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} \\int_{-\\infty}^{\\infty} h(x,y,z,t) e^{+jk_x x} e^{+jk_y y} e^{+jk_z z} e^{-j\\omega t} \\,\\mathrm d x\\,\\mathrm d y\\,\\mathrm d z\\,\\mathrm d t$$\n",
142 | "\n",
143 | "## Sound Propagation as a Linear System\n",
144 | "\n",
145 | "Remember the inhomogeneous wave equation?\n",
146 | "\n",
147 | "$$\\Delta p(\\mathbf x, t) - \\frac{1}{c^2} \\frac{\\partial^2}{\\partial t^2} p(\\mathbf x, t) = -q(\\mathbf x, t)$$\n",
148 | "\n",
149 | "Its solution $p(\\mathbf x, t)$ has to incorporate boundary conditions and the inhomogenity $q(\\mathbf x, t)$. It describes sound propagation and can be interpreted as an LTI-system with the source density $q(\\mathbf x, t)$ as the input signal and the sound pressure $p(\\mathbf x, t)$ as the output signal. Remember the Green's function? It corresponds to the special inhomogenity $q(\\mathbf x, t) = \\delta(\\mathbf x - \\mathbf x_0)\\delta(t-t_0)$ and fulfils the boundary conditions. The inhomogeneous wave equation for the Green's functions reads\n",
150 | "\n",
151 | "$$\\Delta g(\\mathbf x | \\mathbf x_0 , t) - \\frac{1}{c^2} \\frac{\\partial^2}{\\partial t^2} g(\\mathbf x | \\mathbf x_0 , t) = - \\delta(\\mathbf x - \\mathbf x_0)\\delta(t)\\,.$$\n",
152 | "\n",
153 | "*Exercise*: What is the impulse response $h(\\mathbf x,\\mathbf x_0,t)$ of the wave equation as a linear system?\n",
154 | "\n",
155 | "Under free-field conditions, i.e. no boundary conditions, the wave equation becomes an LTSI-System with free-field Green's function $g_0(\\mathbf x , t) = \\delta(t - |\\mathbf x|/c)$ as its spatio-temporal impulse responses. In rooms, the impulse response is often subsumed under the term **Room Impulse Response**.\n",
156 | "\n",
157 | "### Measuring a Room Impulse Response\n",
158 | "\n",
159 | "#### A Very Simplistic Procedure\n",
160 | "\n",
161 | "There are several possibilities to excite the room with a signal being close to a Dirac - bursting balloons, gunshots, electrical spark discharges, ...; for simplicity (and for safety reasons), one could use two wooden boards which are clapped together. The signal recorded by a microphone standing at the certain positions $\\mathbf x$ then yields the approximate room impulse response $h(\\mathbf x,\\mathbf x_0,t)$\n",
162 | "\n",
163 | "\n",
164 | "\n",
165 | "That's not ideal - ideally all frequencies should be excited equally - but it shall be sufficient for demonstration purposes. As we will get to know later, that there are *much better* ways to measure room impulse responses!"
166 | ]
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": 2,
171 | "metadata": {
172 | "execution": {
173 | "iopub.execute_input": "2020-11-26T13:10:06.496070Z",
174 | "iopub.status.busy": "2020-11-26T13:10:06.495576Z",
175 | "iopub.status.idle": "2020-11-26T13:10:13.101530Z",
176 | "shell.execute_reply": "2020-11-26T13:10:13.101930Z"
177 | }
178 | },
179 | "outputs": [],
180 | "source": [
181 | "# speech signal\n",
182 | "speech, fs = sf.read(\"data/xmas.wav\")\n",
183 | "# sloppy measured room impulse response\n",
184 | "# (measured with clap seen above, do not harm your ears!)\n",
185 | "rir, fs_rir = sf.read(\"data/rir_clap.wav\")\n",
186 | "\n",
187 | "assert fs == fs_rir # compare sample rates of signal and rir\n",
188 | "speech_clap = signal.fftconvolve(speech, rir) # convolve with respect to time\n",
189 | "# normalize to the same maximum value as the original speech signal:\n",
190 | "speech_clap = tools.normalize(speech_clap, np.max(np.abs(speech)))\n",
191 | "\n",
192 | "sd.play(speech, fs, blocking=True)\n",
193 | "sd.play(speech_clap, fs, blocking=True)"
194 | ]
195 | },
196 | {
197 | "cell_type": "markdown",
198 | "metadata": {},
199 | "source": [
200 | "#### More elaborated procedures\n",
201 | "\n",
202 | "In slightly more modern *sweep method*, room is excited with a sine sweep, which is reproduced by means of a loudspeaker. The actual impulse response will be calculated from the excitation signal and the signal recorded by the microphone. Let's listen to some sweep signals (**watch the volume**!):"
203 | ]
204 | },
205 | {
206 | "cell_type": "code",
207 | "execution_count": 3,
208 | "metadata": {
209 | "execution": {
210 | "iopub.execute_input": "2020-11-26T13:10:13.105844Z",
211 | "iopub.status.busy": "2020-11-26T13:10:13.105398Z",
212 | "iopub.status.idle": "2020-11-26T13:10:19.392534Z",
213 | "shell.execute_reply": "2020-11-26T13:10:19.392948Z"
214 | }
215 | },
216 | "outputs": [],
217 | "source": [
218 | "fstart = 65 # start with 65 Hertz, be nice to your onboard loudspeakers\n",
219 | "fstop = 15000 # stop a 15000 Hertz\n",
220 | "tlen = 3 # length of sweep signal\n",
221 | "t = np.arange(0, tlen*fs)/fs # samples\n",
222 | "\n",
223 | "sweep_lin = signal.chirp(t, fstart, tlen, fstop, method='linear')\n",
224 | "sweep_log = signal.chirp(t, fstart, tlen, fstop, method='logarithmic')\n",
225 | "\n",
226 | "sd.play(sweep_lin, fs, blocking=True)\n",
227 | "sd.play(sweep_log, fs, blocking=True)"
228 | ]
229 | },
230 | {
231 | "cell_type": "markdown",
232 | "metadata": {},
233 | "source": [
234 | "## Fourier Analysis of a Sound Field\n",
235 | "\n",
236 | "### Spatial-Temporal Fourier Transform of a Plane Wave\n",
237 | "\n",
238 | "We already got to know the temporal spectrum of a [plane wave](physics_of_sound_I-solutions.ipynb#Plane-Wave) with a direction of propagation $\\mathbf n_{\\mathrm {pw}}$ which reads\n",
239 | "\n",
240 | "$$P_{pw}(\\mathbf x, \\omega) = \\mathrm{exp}\\left(-j\\frac{\\omega}{c} \\mathbf n_{\\mathrm {pw}} \\cdot \\mathbf x \\right) = \\mathrm{exp}\\left(-j\\frac{\\omega}{c} (n_{\\mathrm {pw},x} \\cdot x + n_{\\mathrm {pw},y} \\cdot y + n_{\\mathrm {pw},z} \\cdot z) \\right) \\,. $$\n",
241 | "\n",
242 | "*Exercise*: Calculate the three-dimensional, spatial Fourier Transform $\\tilde P_{pw}(\\mathbf k, \\omega)$ of the Plane Wave. Use the integral identity:\n",
243 | "\n",
244 | "$$ \\int_{-\\infty}^{\\infty} e^{+j a b}\\,\\mathrm d b\n",
245 | " = \\int_{-\\infty}^{\\infty} \\mathrm{exp}\\left(+j a b\\right)\\,\\mathrm d b \n",
246 | " = 2\\pi \\delta(a) \n",
247 | "$$"
248 | ]
249 | },
250 | {
251 | "cell_type": "markdown",
252 | "metadata": {
253 | "collapsed": true
254 | },
255 | "source": [
256 | " "
257 | ]
258 | },
259 | {
260 | "cell_type": "markdown",
261 | "metadata": {},
262 | "source": [
263 | "### Fourier Analysis using a Plane Wave Decomposition\n",
264 | "\n",
265 | "We now know that the value of $\\tilde P(\\mathbf k, \\omega)$ at $\\mathbf k = \\frac{\\omega}{c} \\mathbf n_{\\mathrm {pw}}$ corresponds to the amplitude of the plane wave propagation in the directions of $\\mathbf n_{\\mathrm {pw}}$. This can be straightforwardly used to compute the so-called plane wave composition:\n",
266 | "\n",
267 | "$$\n",
268 | "\\bar P(\\mathbf n_{\\mathrm {pw}}) = \\tilde P(\\mathbf k, \\omega)|_{\\mathbf k = \\frac{\\omega}{c} \\mathbf n_{\\mathrm {pw}}}\n",
269 | "$$\n",
270 | "\n",
271 | "After all this math, u can lay back and have a look at the demonstration, how to compute a plane wave decompositon out of two microphone signals.\n",
272 | "\n"
273 | ]
274 | },
275 | {
276 | "cell_type": "markdown",
277 | "metadata": {},
278 | "source": [
279 | "## Solutions\n",
280 | "\n",
281 | "If you had problems solving some of the exercises, don't despair!\n",
282 | "Have a look at the [example solutions](linear_systems_II-solutions.ipynb)."
283 | ]
284 | },
285 | {
286 | "cell_type": "markdown",
287 | "metadata": {},
288 | "source": [
289 | "
\n",
290 | " \n",
292 | " \n",
293 | " \n",
294 | " \n",
295 | " To the extent possible under law,\n",
296 | " the person who associated CC0\n",
297 | " with this work has waived all copyright and related or neighboring\n",
298 | " rights to this work.\n",
299 | "
"
300 | ]
301 | }
302 | ],
303 | "metadata": {
304 | "kernelspec": {
305 | "display_name": "mystiasp",
306 | "language": "python",
307 | "name": "mystiasp"
308 | },
309 | "language_info": {
310 | "codemirror_mode": {
311 | "name": "ipython",
312 | "version": 3
313 | },
314 | "file_extension": ".py",
315 | "mimetype": "text/x-python",
316 | "name": "python",
317 | "nbconvert_exporter": "python",
318 | "pygments_lexer": "ipython3",
319 | "version": "3.7.8"
320 | }
321 | },
322 | "nbformat": 4,
323 | "nbformat_minor": 1
324 | }
325 |
--------------------------------------------------------------------------------
/plot_direction.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """Plot the live microphone signal(s) with matplotlib."""
3 | import argparse
4 | from queue import Queue, Empty
5 |
6 |
7 | def int_or_str(text):
8 | """Helper function for argument parsing."""
9 | try:
10 | return int(text)
11 | except ValueError:
12 | return text
13 |
14 |
15 | parser = argparse.ArgumentParser(description=__doc__)
16 | parser.add_argument(
17 | '-l', '--list-devices', action='store_true',
18 | help='show list of audio devices and exit')
19 | parser.add_argument(
20 | '-d', '--device', type=int_or_str,
21 | help='input device (numeric ID or substring)')
22 | parser.add_argument(
23 | '-w', '--window', type=float, default=200, metavar='DURATION',
24 | help='visible time slot (default: %(default)s ms)')
25 | parser.add_argument(
26 | '-i', '--interval', type=float, default=30,
27 | help='minimum time between plot updates (default: %(default)s ms)')
28 | parser.add_argument(
29 | '-b', '--blocksize', type=int, help='block size (in samples)')
30 | parser.add_argument(
31 | '-r', '--samplerate', type=float, help='sampling rate of audio device')
32 | parser.add_argument(
33 | '-n', '--downsample', type=int, default=1, metavar='N',
34 | help='display every Nth sample (default: %(default)s)')
35 | parser.add_argument(
36 | 'channels', type=int, default=[1,2], nargs='*', metavar='CHANNEL',
37 | help='input channels for estimation of direction (default: the first two)')
38 | parser.add_argument(
39 | '-D', '--distance', type=float, default=0.2, metavar='DISTANCE',
40 | help='distance of the two microphones channels (default: $(default) m)')
41 | args = parser.parse_args()
42 | if any(c < 1 for c in args.channels):
43 | parser.error('argument CHANNEL: must be >= 1')
44 | mapping = [c - 1 for c in args.channels] # Channel numbers start with 1
45 | queue = Queue()
46 |
47 |
48 | def audio_callback(indata, frames, time, status):
49 | """This is called (from a separate thread) for each audio block."""
50 | if status:
51 | print(status, flush=True)
52 | # Fancy indexing with mapping creates a (necessary!) copy:
53 | queue.put(indata[::args.downsample, mapping])
54 |
55 |
56 | def update_plot(frame):
57 | """This is called by matplotlib for each plot update.
58 |
59 | Typically, audio callbacks happen more frequently than plot updates,
60 | therefore the queue tends to contain multiple blocks of audio data.
61 |
62 | """
63 | global newdata
64 | global corr
65 | block = True # The first read from the queue is blocking ...
66 |
67 | while True:
68 | try:
69 | data = queue.get(block=block)
70 | except Empty:
71 | break
72 | shift = len(data)
73 |
74 | newdata = np.roll(newdata, -shift, axis=0)
75 | newdata[-shift:,:] = data
76 | block=False # ... all further reads are non-blocking
77 |
78 | corr = np.correlate(newdata[:,0], newdata[:,1], mode='full')
79 | corr = np.abs(corr)/np.max(np.max(corr))
80 |
81 | lines[0].set_ydata(corr)
82 |
83 | return lines
84 |
85 | try:
86 | from matplotlib.animation import FuncAnimation
87 | import matplotlib.pyplot as plt
88 | import numpy as np
89 | import sounddevice as sd
90 |
91 | if args.list_devices:
92 | print(sd.query_devices())
93 | parser.exit()
94 | if args.samplerate is None:
95 | device_info = sd.query_devices(args.device, 'input')
96 | args.samplerate = device_info['default_samplerate']
97 |
98 | length = int(args.window * args.samplerate/ (1000 * args.downsample))
99 | ran = args.distance/343*args.samplerate/args.downsample
100 | newdata = np.zeros((length, len(args.channels)))
101 | corr = np.zeros((2*length-1))
102 |
103 | phi = np.linspace(0, 180, 6);
104 | phi = np.append(phi, 90)
105 | xtics = ran*np.cos(phi*np.pi/180) +length-1
106 | xlabels = phi
107 |
108 | fig, ax = plt.subplots()
109 | lines = ax.plot(corr)
110 | ax.axis((-ran+length-1, ran+length-1, 0, 1))
111 | ax.set_xticks(xtics)
112 | ax.set_xticklabels(xlabels)
113 | ax.yaxis.grid(True)
114 | fig.tight_layout(pad=0)
115 |
116 | stream = sd.InputStream(
117 | device=args.device, channels=max(args.channels),
118 | samplerate=args.samplerate, callback=audio_callback)
119 | ani = FuncAnimation(fig, update_plot, interval=args.interval, blit=True)
120 | with stream:
121 | plt.show()
122 | except Exception as e:
123 | parser.exit(type(e).__name__ + ': ' + str(e))
124 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | jupyter
2 | notebook
3 | ipykernel
4 | matplotlib==3.1
5 | numpy
6 | scipy
7 | sfs==0.5.0
8 | sounddevice
9 | soundfile
10 |
--------------------------------------------------------------------------------
/tools.py:
--------------------------------------------------------------------------------
1 | """Some tools used in the communication acoustics exercises."""
2 | from __future__ import division # Only needed for Python 2.x
3 | import numpy as np
4 | import os
5 | from scipy import signal
6 | try:
7 | from urllib.request import Request, urlopen # Python 3.x
8 | except ImportError:
9 | from urllib2 import Request, urlopen # Python 2.x
10 |
11 |
12 | def normalize(x, maximum=1, axis=None, out=None):
13 | """Normalize a signal to the given maximum (absolute) value.
14 |
15 | Parameters
16 | ----------
17 | x : array_like
18 | Input signal.
19 | maximum : float or sequence of floats, optional
20 | Desired (absolute) maximum value. By default, the signal is
21 | normalized to +-1.0. If a sequence is given, it must have the
22 | same length as the dimension given by `axis`. Each sub-array
23 | along the given axis is normalized with one of the values.
24 | axis : int, optional
25 | Normalize along a given axis.
26 | By default, the flattened array is normalized.
27 | out : numpy.ndarray or similar, optional
28 | If given, the result is stored in `out` and `out` is returned.
29 | If `out` points to the same memory as `x`, the normalization
30 | happens in-place.
31 |
32 | Returns
33 | -------
34 | numpy.ndarray
35 | The normalized signal.
36 |
37 | """
38 | if axis is None and not np.isscalar(maximum):
39 | raise TypeError("If axis is not specified, maximum must be a scalar")
40 |
41 | maximum = np.max(np.abs(x), axis=axis) / maximum
42 | if axis is not None:
43 | maximum = np.expand_dims(maximum, axis=axis)
44 | return np.true_divide(x, maximum, out)
45 |
46 |
47 | def fade(x, in_length, out_length=None, type='l', copy=True):
48 | """Apply fade in/out to a signal.
49 |
50 | If `x` is two-dimenstional, this works along the columns (= first
51 | axis).
52 |
53 | This is based on the *fade* effect of SoX, see:
54 | http://sox.sourceforge.net/sox.html
55 |
56 | The C implementation can be found here:
57 | http://sourceforge.net/p/sox/code/ci/master/tree/src/fade.c
58 |
59 | Parameters
60 | ----------
61 | x : array_like
62 | Input signal.
63 | in_length : int
64 | Length of fade-in in samples (contrary to SoX, where this is
65 | specified in seconds).
66 | out_length : int, optional
67 | Length of fade-out in samples. If not specified, `fade_in` is
68 | used also for the fade-out.
69 | type : {'t', 'q', 'h', 'l', 'p'}, optional
70 | Select the shape of the fade curve: 'q' for quarter of a sine
71 | wave, 'h' for half a sine wave, 't' for linear ("triangular")
72 | slope, 'l' for logarithmic, and 'p' for inverted parabola.
73 | The default is logarithmic.
74 | copy : bool, optional
75 | If `False`, the fade is applied in-place and a reference to
76 | `x` is returned.
77 |
78 | """
79 | x = np.array(x, copy=copy)
80 |
81 | if out_length is None:
82 | out_length = in_length
83 |
84 | def make_fade(length, type):
85 | fade = np.arange(length) / length
86 | if type == 't': # triangle
87 | pass
88 | elif type == 'q': # quarter of sinewave
89 | fade = np.sin(fade * np.pi / 2)
90 | elif type == 'h': # half of sinewave... eh cosine wave
91 | fade = (1 - np.cos(fade * np.pi)) / 2
92 | elif type == 'l': # logarithmic
93 | fade = np.power(0.1, (1 - fade) * 5) # 5 means 100 db attenuation
94 | elif type == 'p': # inverted parabola
95 | fade = (1 - (1 - fade)**2)
96 | else:
97 | raise ValueError("Unknown fade type {0!r}".format(type))
98 | return fade
99 |
100 | # Using .T w/o [:] causes error: https://github.com/numpy/numpy/issues/2667
101 | x[:in_length].T[:] *= make_fade(in_length, type)
102 | x[len(x) - out_length:].T[:] *= make_fade(out_length, type)[::-1]
103 | return x
104 |
105 |
106 | def db(x, power=False):
107 | """Convert a signal to decibel.
108 |
109 | Parameters
110 | ----------
111 | x : array_like
112 | Input signal. Values of 0 lead to negative infinity.
113 | power : bool, optional
114 | If `power=False` (the default), `x` is squared before
115 | conversion.
116 |
117 | """
118 | with np.errstate(divide='ignore'):
119 | return 10 if power else 20 * np.log10(np.abs(x))
120 |
121 |
122 | def blackbox(x, samplerate, axis=0):
123 | """Some unknown (except that it's LTI) digital system.
124 |
125 | Parameters
126 | ----------
127 | x : array_like
128 | Input signal.
129 | samplerate : float
130 | Sampling rate in Hertz.
131 | axis : int, optional
132 | The axis of the input data array along which to apply the
133 | system. By default, this is the first axis.
134 |
135 | Returns
136 | -------
137 | numpy.ndarray
138 | The output signal.
139 |
140 | """
141 | # You are not supposed to look!
142 | b, a = signal.cheby1(8, 0.1, 3400 * 2 / samplerate)
143 | x = signal.lfilter(b, a, x, axis)
144 | b, a = signal.cheby1(4, 0.1, 300 * 2 / samplerate, 'high')
145 | return signal.lfilter(b, a, x, axis)
146 |
147 |
148 | def blackbox_nonlinear(x, samplerate, axis=0):
149 | """Some unknown (except that it's non-linear) digital system.
150 |
151 | See Also
152 | --------
153 | blackbox
154 |
155 | """
156 | # You are not supposed to look!
157 | thr = 1/7
158 | out = blackbox(x, samplerate, axis)
159 | x = np.max(np.abs(out)) * thr
160 | return np.clip(out, -x, x, out=out)
161 |
162 |
163 | class HttpFile(object):
164 | """based on http://stackoverflow.com/a/7852229/500098"""
165 |
166 | def __init__(self, url):
167 | self._url = url
168 | self._offset = 0
169 | self._content_length = None
170 |
171 | def __len__(self):
172 | if self._content_length is None:
173 | response = urlopen(self._url)
174 | self._content_length = int(response.headers["Content-length"])
175 | return self._content_length
176 |
177 | def read(self, size=-1):
178 | request = Request(self._url)
179 | if size < 0:
180 | end = len(self) - 1
181 | else:
182 | end = self._offset + size - 1
183 | request.add_header('Range', "bytes={0}-{1}".format(self._offset, end))
184 | data = urlopen(request).read()
185 | self._offset += len(data)
186 | return data
187 |
188 | def seek(self, offset, whence=os.SEEK_SET):
189 | if whence == os.SEEK_SET:
190 | self._offset = offset
191 | elif whence == os.SEEK_CUR:
192 | self._offset += offset
193 | elif whence == os.SEEK_END:
194 | self._offset = len(self) + offset
195 | else:
196 | raise ValueError("Invalid whence")
197 |
198 | def tell(self):
199 | return self._offset
200 |
--------------------------------------------------------------------------------