{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to Python et al., Working with Audio Signals\n", "\n", "[return to main page](index.ipynb)\n", "\n", "For most of the exercises, we will use the very popular programming language [Python](https://www.python.org) together with a few external libraries from the [Scientific Python Stack](http://scipy.org).\n", "To get started, you might also want to have a look a those:\n", "\n", "* [Python Introduction](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/intro-python.ipynb) (pure Python, no NumPy)\n", "\n", "* [Simple Signal Processing Example](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/simple-signals.ipynb) (quite similar to the things on this very page)\n", "\n", "Note that Python is not the only option for the kind of tasks that we'll tackle here.\n", "If you are interested in some alternatives, have a look at [Julia](http://julialang.org/), [R](http://www.r-project.org/), [Octave](http://octave.org/) or [Scilab](http://www.scilab.org/).\n", "All the mentioned applications are open-source software and there are of course even more alternatives (both free and proprietary).\n", "\n", "Most of the exercises in this course (including the one you're reading right now) are presented as [Jupyter (formerly known as IPython) notebooks](http://jupyter.org/).\n", "They can be [viewed online](http://nbviewer.jupyter.org/github/spatialaudio/communication-acoustics-exercises/blob/master/index.ipynb), but it makes much more sense to download them and open and explore them locally with [Jupyter](http://jupyter.org/).\n", "\n", "For installation instructions, see the section [Getting Started](index.ipynb#Getting-Started) on the main page.\n", "\n", "To get an idea what Jupyter is all about, have a look at this [Jupyter Introduction](http://nbviewer.jupyter.org/github/mgeier/python-audio/blob/master/intro-jupyter.ipynb).\n", "To use this very notebook, start the Jupyter Notebook server (which will open a browser window for you) with the command:\n", "\n", " python3 -m notebook\n", "\n", "ATTENTION: always make sure to run Python version 3.x and *not* Python 2.x!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What Will We Learn Today?\n", "\n", "* Basics of Python, Jupyter/IPython, NumPy, SciPy, matplotlib and some other external libraries\n", "\n", "* How to create a simple audio signal\n", "\n", "* How to plot this signal\n", "\n", "* How to listen to this signal\n", "\n", "* How to save this signal to the hard disk (as a WAV file)\n", "\n", "* How to handle signals with more than one channel\n", "\n", "* A quick demonstration of the *interaural time difference* (ITD)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Notebook Cells\n", "\n", "This notebook consists of so-called \"cells\", which can be used for normal text (see above) or for Python code (see below).\n", "*Code cells* can be selected by a mouse click (or with the arrow up/down keys and *Enter*), the code can be edited and then executed by pressing *Shift+Enter* or by clicking the button in the top part of the page.\n", "\n", "Don't be shy, try it:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "50 - 5 * 4 + 12" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Code cells can have multiple lines (use *Enter* for line breaks).\n", "When the code cell is executed, all lines are executed, but only the value of the last line is displayed (except if there is no value to display).\n", "\n", "Here's another code cell for you to play with:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "New cells can be inserted by pressing the *a* or *b* keys (to insert *above* or *below* the current cell) or via the menu. You should also have a look at \"Help\" -> \"Keyboard Shortcuts\".\n", "\n", "You can step through all cells in the notebook by repeatedly pressing *Shift+Enter*.\n", "Alternatively, you can click \"Run All\" in the \"Cell\" menu." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Importing Modules/Packages\n", "\n", "In order to work with numeric arrays (in our case mainly audio signals), we import the [NumPy](http://www.numpy.org) package." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can use all NumPy functions (by prefixing \"`np.`\")." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.zeros(10000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tab Completion\n", "\n", "*Exercise:* Type \"`np.ze`\" (without the quotes) and then hit the *Tab* key ..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Array, Vector, Matrix\n", "\n", "Audio signals can be stored in NumPy *arrays*.\n", "Arrays can have arbitrarily many dimensions, but let's use only one-dimensional arrays for now.\n", "Arrays can be created with [numpy.array()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the result is not displayed when you assign to a variable (because assignment is a *statement* and not an *expression*).\n", "To show the data, write the variable name separately as the last (or only) line of a code cell." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "BTW, there is an easier way to get this particular array (using [numpy.arange()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.arange.html)):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b = np.arange(10)\n", "b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the range starts with `0` and ends just before the given stop value!\n", "\n", "If you are not used to programming, this might seem strange at first sight, but you'll see that this is vastly superior to starting with `1` and including the stop value.\n", "If you're not convinced yet, have a look at [what E. W. Dijkstra has to say](http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting Help\n", "\n", "If you want to know details about the usage of `np.arange()` and all its supported arguments, have a look at its help text.\n", "Just append a question mark to the function name (without parentheses!):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.arange?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A help window should open in the lower part of the browser window.\n", "This window can be closed by hitting the *q* key (like \"quit\").\n", "\n", "Let's get some more help:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.zeros?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also get help for the whole NumPy package:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can get help for any object by appending (or prepending) a question mark to the name of the object.\n", "Let's check what the help system can tell us about our variable `a`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The help system may come in handy when solving the following exercises ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `np.arange()`\n", "\n", "We'll often need sequences of evenly spaced numbers, so let's create some.\n", "\n", "*Exercise:* Create a sequence of numbers with `np.arange()`, starting with 0 and up to (but not including) 6 with a step size of 1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a sequence of numbers with `np.arange()`, starting with 0 and up to (but not including) 0.6 with a step size of 0.1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a sequence of numbers with `np.arange()`, starting with 0.5 and up to (but not including) 1.1 with a step size of 0.1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The previous exercise is somewhat tricky.\n", "If you got it right, have a look at [arange considered harmful](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/misc/arange.ipynb) for what you *could have* done wrong.\n", "If you got an unexpected result, have a look at [arange considered harmful](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/misc/arange.ipynb) for an explanation.\n", "\n", "*Exercise:* Can you fix the problem?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What do we learn from all this?\n", "$\\Rightarrow$\n", "`np.arange()` is great, but use it only with integer step sizes!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `np.linspace()`\n", "\n", "Another, slightly different method to create a sequence of evenly spaced numbers is [numpy.linspace()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html).\n", "Have a look at the documentation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.linspace?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a sequence of numbers with `np.linspace()`, starting with 0 and up to (including) 6 with a step size of 1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the resulting array will have a *floating point* data type even if all inputs (and the step size) are integers.\n", "This is not the case with `np.arange()`.\n", "\n", "*Exercise:* Create a sequence of numbers with `np.linspace()`, starting with 0 and up to (but not including) 6 with a step size of 1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a sequence of numbers with `np.linspace()`, starting with 0 and up to (but not including) 0.6 with a step size of 0.1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a sequence of numbers with `np.linspace()`, starting with 0.5 and up to (but not including) 1.1 with a step size of 0.1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that `np.linspace()` doesn't have the above-mentioned problem we had with `np.arange()`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `np.arange()` vs. `np.linspace()`\n", "\n", "*Exercise:* Find some examples where `np.array()` works better and some where `np.linspace()` should be preferred." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A Shorthand for `np.arange()` and `np.linspace()`\n", "\n", "If you want to save a few keystrokes and make your code a little more obscure, have a look at [numpy.r_[]](http://docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.r_?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a Sine Tone\n", "\n", "Now let's create our first audio signal, shall we?\n", "\n", "Let's generate a signal using the equation $y(t) = A\\sin(\\omega t)$ with $\\omega = 2\\pi f$ and $f$ being the frequency of the sine tone.\n", "The maximum signal amplitude is given by $A$.\n", "The variable $t$ obviously represents time.\n", "Let's create a digital signal with evenly spaced values for $t$.\n", "\n", "We can use the function [numpy.sin()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.sin.html) to create a sine tone. Let's look at its help text first." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.sin?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we know which function to call, we need appropriate input.\n", "And that's where our sequences of evenly spaced values from above come into play.\n", "\n", "The nice thing about NumPy functions like `np.sin()` is that they can operate on whole arrays at once, so it is not necessary to call the function on each single value separately.\n", "Therefore, we can store the whole range of values for our time variable $t$ in one array.\n", "\n", "According to the equation, each value of $t$ has to be multiplied with (the constant) $\\omega$.\n", "That's another nice thing about NumPy: we don't have to multiply every value of the array $t$ separately with $\\omega$, we can multiply the whole array with a scalar at once, and NumPy does the element-wise multiplication for us.\n", "This is called [\"broadcasting\"](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html), in case you stumble upon that word in the docs.\n", "The array returned by `np.sin()` can (again using broadcasting) be multiplied by the constant scalar $A$ to get the final result.\n", "\n", "The only thing that's still missing is $\\pi$, but that's simple:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.pi" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a sine tone with a frequency of 500 Hz, a duration of 1 second and an amplitude of 0.3.\n", "Use a sampling rate of 44.1 kHz.\n", " The first value of $t$ should be 0." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dur = 1 # duration in seconds\n", "amp = 0.3 # maximum amplitude\n", "freq = 500 # frequency of the sine tone in Hertz\n", "fs = 44100 # sampling frequency in Hertz\n", "\n", "# t = ???\n", "\n", "# y = ??? * np.sin(??? * t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happens if `dur` is not an integer? \n", "What happens if `dur` is not an integer multiple of `1/fs`?\n", "\n", "*Exercise:* Try if your code still works in those cases." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Use the [built-in len() function](https://docs.python.org/3/library/functions.html#len) to find out how many samples the resulting signal has.\n", "How many is it supposed to have?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting\n", "\n", "Python and NumPy cannot plot by themselves, they need some help from [matplotlib](http://matplotlib.org/)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can plot the data from our array:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The peculiar thing about `matplotlib` is that it doesn't actually show the plot!\n", "\n", "But it's in there somewhere ... we just have to use `plt.show()` to reveal it:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the plot window should be visible.\n", "Check out [all those fancy buttons](http://matplotlib.org/users/navigation_toolbar.html)!\n", "\n", "*Exercise:* Zoom into the plot so that only one period of the sine tone is visible.\n", "There are different ways to do that.\n", "\n", "Note that while the plot window is open, we cannot use any of the code cells in our notebook.\n", "You have to close the plot window to be able to continue.\n", "This is annoying.\n", "As is typing `plt.show()` all the time.\n", "\n", "That's why the Jupyter/IPython notebook provides this special (a.k.a. \"magic\") command:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After that, you don't have to use `plt.show()` anymore, and you can run code cells even if the plot window is open.\n", "\n", "Try it:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice, isn't it?\n", "\n", "There is still something ugly going on:\n", "The plotting functions return some strange object(s) which we don't need (for now).\n", "Those can be ignored by appending a semicolon to the last statement of a code cell (is has no effect after any other statement):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(y);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Very nice!\n", "\n", "The plot window is very useful for quick interactive exploration, but sometimes you might want to save the plots within the notebook.\n", "That can be done in *inline mode*:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Anything you plot now, will be shown right below the code cell.\n", "Let's only plot a part of the signal this time." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(y[:300]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inline plots are saved (as images) within the notebook.\n", "\n", "If you want to use the plot window again, just do:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As always, for more info:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a similar \"magic\" command which may come in handy for quickly trying stuff without having to type all those imports and all the `np.` and `plt.` prefixes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pylab?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, this is not recommended for \"serious\" notebooks, because it makes the code harder to read and it may lead to confusion between functions with the same name (but different semantics) from different namespaces (`sum()` vs. `np.sum()`, `max()` vs. `np.max()`, `all()` vs. `np.all()` etc.)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tweaking the Plot\n", "\n", "Let's look again at our plot." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(y);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we passed only a single array to the `plot()` function, the x-axis shows the sample index from 0 to the length of the signal in samples (minus one).\n", "It may be more meaningful to show the time in seconds.\n", "\n", "But let's close the previous plot first." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we pass two arrays to the `plot()` function, the first one defines the mapping from sample indices to the actual values displayed on the x-axis, the second one specifies the corresponding y values." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(t, y);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Good, now the x-axis shows the time in seconds.\n", "Let's create axis labels so that everyone knows." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.xlabel(\"Time / Seconds\")\n", "plt.ylabel(\"Amplitude\")\n", "plt.title(\"Sine Tone with {} Hz\".format(freq));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more information, have a look at [Getting Started With `matplotlib`](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/plotting/matplotlib.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Listening to the Signal\n", "\n", "Python cannot play audio on its own, but there are several external libraries available for that.\n", "We'll be using [python-sounddevice](http://python-sounddevice.rtfd.org/), some other libraries are shown at [this overview page](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/playback-recording/index.ipynb).\n", "\n", "To install the `sounddevice` module, just follow its installation instructions.\n", "In many cases, it'll boil down to:\n", "\n", " python3 -m pip install sounddevice --user\n", "\n", "Note: After the installation, you should restart any running IPython kernels (e.g. using the menu \"Kernel\" $\\to$ \"Restart\"), otherwise they won't know about the newly installed Python module.\n", "After restarting, you can use the menu option \"Cell\" $\\to$ \"Run All Above\" to re-evaluate everything you've written above.\n", "\n", "Once the library is installed, you can use it like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sounddevice as sd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "WARNING: You should turn the volume down, just to be sure not to destroy your loudspeakers/headphones/ears." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sd.play(y, fs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* It's possible that you hear clicks in the beginning/end. What could be the reason for that? How could that be mitigated?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Have a look in the file [tools.py](tools.py) (in the current directory), probably you can find something useful there?\n", "\n", "The clicks can be really annoying, it would be great if we could get rid of them automatically each time we play some signal.\n", "To achieve that, let's create our own personal playback function (we'll learn more about creating functions in a minute ...):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import tools\n", "\n", "def myplay(data):\n", " \"\"\"Apply fade in/out and play with 44.1 kHz.\"\"\"\n", " data = tools.fade(data, 2000, 5000)\n", " sd.play(data, 44100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's try if that works:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myplay(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Try different values for the fade in/out lengths and listen to the effect." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Writing the Signal to a File\n", "\n", "It's possible to write WAV files with the [wave module](https://docs.python.org/3/library/wave.html) from Python's standard library.\n", "Since this is [quite complicated](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/audio-files/audio-files-with-wave.ipynb#Writing), we'll use the more convenient external library [PySoundFile](https://github.com/bastibe/PySoundFile/).\n", "Of course there are also alternatives; have a look at [this overview page](http://nbviewer.ipython.org/github/mgeier/python-audio/blob/master/audio-files/index.ipynb).\n", "\n", "Have a look at [the documentation](http://pysoundfile.readthedocs.org/) for how to install PySoundFile.\n", "If you have [pip](https://pip.pypa.io/en/stable/installing.html) and the library [libsndfile](http://www.mega-nerd.com/libsndfile/) already installed, it should be enough to run this command in a terminal:\n", "\n", " python3 -m pip install PySoundFile --user\n", "\n", "Again, you will have to restart the IPython kernel (e.g. using the menu \"Kernel\" $\\to$ \"Restart\") to be able to import the newly installed Python module.\n", "Afterwards, you might want to use the menu command \"Cell\" $\\to$ \"Run All Above\" to re-create your signal.\n", "\n", "Once installed, you can use it like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import soundfile as sf" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sf.write('my_first_signal.wav', y, fs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the sampling rate has to be passed to be stored within the file.\n", "\n", "*Exercise:* Find the sound file that was just written and play it in an external audio player ([play](http://sox.sourceforge.net/), [aplay](http://linux.die.net/man/1/aplay), [mplayer](https://www.mplayerhq.hu/), [audacious](http://audacious-media-player.org/), [aqualung](http://aqualung.factorial.hu/), [vlc](http://www.videolan.org/vlc/), ...).\n", "\n", "*Exercise:* Here we have the same problem with clicks at the beginning/end.\n", "Apply a fade in/out before writing the signal to the file." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a Function\n", "\n", "Generating our sine tone wasn't very complicated, but let's still create a function that does all the steps for us.\n", "We can then use this function repeatedly to create several different sine tones.\n", "\n", "*Exercise:* Create a function named `mysine()` based on the template below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def mysine(frequency, amplitude, duration):\n", " \"\"\"Generate sine tone with the given parameters @ 44.1 kHz.\"\"\"\n", " samplerate = 44100\n", " \n", " # add your code here!\n", "\n", " return ... # ???" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note the indentation of 4 spaces.\n", "Every statement inside the function must start with the same indentation (except if you use statements which themselves need additional indentation, like a `for` loop).\n", "\n", "The string below the first line is called \"docstring\".\n", "Every function should have that.\n", "The docstring can be shown with `help(mysine)` or, as we've seen before, by appending a question mark to the name (the former works in all Python interpreters, the latter only in Jupyter/IPython):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mysine?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* See what happens if you use two question marks." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Check if your function works by calling it with a few different input values and plotting the results." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating another Function (for Plotting)\n", "\n", "*Exercise:* Create a function that takes a signal (as a NumPy array) and creates a plot with axis labels and with the x-axis showing the time in seconds.\n", "Like before, let's use a fixed sampling rate of 44.1 kHz." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Using our two functions, create a sine tone and plot it, all in one line." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `for` Loops\n", "\n", "In the next exercise, you'll have to do very similar things multiple times in a row.\n", "That sounds like a job for a `for` loop.\n", "\n", "That's how `for` loops work in Python:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for i in \"one\", 2, \"III\":\n", " # code within the loop body uses 4 spaces for indentation\n", " # ...\n", " print(\"i:\", i)\n", " # ...\n", "# de-indent to continue after the loop\n", "print(\"finished!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Use a `for` loop to call the function `mysine()` three times with frequencies of your choice and store the results in three WAV files that have the respective frequencies in their file names.\n", "Use [the format method of `str` objects](https://docs.python.org/3/library/stdtypes.html#str.format) to create the file names.\n", "Don't forget to use `tools.fade()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Listen to the WAV files to check if everything is OK." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Signal Processing with the *SciPy* Library\n", "\n", "The name \"SciPy\" stands for two slightly different things:\n", "\n", "* The [*Scientific Python Ecosystem*](http://scipy.org/), consisting of [NumPy](http://numpy.scipy.org/), [matplotlib](http://matplotlib.org/), [Jupyter](http://jupyter.org/)/[IPython](http://ipython.org/), the [SciPy library](http://scipy.org/scipylib/) and many more libraries and tools.\n", "\n", "* The [SciPy library](http://docs.scipy.org/doc/scipy/reference/tutorial/general.html), which in turn is part of the *Scientific Python Ecosystem*.\n", "\n", "We were using the former already, now let's use the latter:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from scipy import signal" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This imports SciPy's [signal processing module](http://docs.scipy.org/doc/scipy/reference/tutorial/signal.html).\n", "\n", "Unlike \"`import numpy as np`\" we *never* import the whole `scipy` namespace.\n", "The SciPy package is a collection of [many sub-packages and sub-modules](http://docs.scipy.org/doc/scipy/reference/); we only import those that we need.\n", "\n", "You should *always* use one of those forms:\n", "\n", " from scipy import foobar\n", " import scipy.foobar as foo\n", " from scipy.foobar import foofun\n", "\n", "... and *never* one of those:\n", "\n", "
\n",
    "import scipy\n",
    "from scipy import foofun\n",
    "
\n", "\n", "... where `foobar` is the name of a sub-package/sub-module and `foofun` is the name of a function.\n", "\n", "*Exercise:* Use the SciPy function [signal.chirp()](http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.chirp.html) to create a linear sine sweep with an initial frequency of 100 Hz, a final frequency of 5000 Hz, with a length of 2 seconds and with an amplitude of 0.2.\n", "Listen to the results." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Change the sweep type from 'linear' to 'log' and listen to the results." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll see more of SciPy in later exercises, stay tuned!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Superposition of Signals\n", "\n", "Let's use our function `mysine()` from above to create a sine tone.\n", "You can use the given parameters or change them to something else, if you prefer:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sinetone = mysine(frequency=500, amplitude=0.3, duration=1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a noise signal of the same length using the function [numpy.random.normal()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html).\n", "Use `scale=0.1` or at least something smaller than the amplitude of the sine tone." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create the superposition of the two signals and listen to it.\n", "Plot it, too." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Use the function `mysine()` (two times) to create a superposition of a sine tone with a frequency of 500 Hz and another sine tone with a frequency of 507 Hz - each with an `amplitude=0.2`.\n", "What do you notice when listening to the signal?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Plot the signal and try to *see* what you *heard* before." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More Than One Channel $\\to$ Two-Dimensional Arrays\n", "\n", "Up to now, we were only using audio signals with a single channel.\n", "Those could be easily stored in a one-dimensional NumPy array.\n", "\n", "To store more than one channel, we can use a two-dimensional array.\n", "Two-dimensional arrays somewhat look like lists of lists, but internally, they are still stored in one contiguous area of memory.\n", "\n", "There are several functions for creating arrays which allow to specify the number of rows and columns, e.g. [numpy.zeros()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros.html) and [numpy.ones()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.ones.html)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.zeros((4, 2))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.ones((4, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Arrays can also be created from lists of lists (a.k.a. *nested* lists) with [numpy.array()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.array([[.1, .2], [.3, .4], [.5, .6], [.7, .8]])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the inner lists provide the individual rows of the array.\n", "\n", "Two-dimensional arrays can also be created by concatenating a list of one-dimensional arrays (or lists) by columns using [numpy.column_stack()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.column_stack.html):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = np.column_stack([[.1, .2, .3, .4], [.5, .6, .7, .8]])\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is common to store the channels of a multi-channel signal as the columns of an array.\n", "This is not guaranteed, though, you might also encounter functions that expect the channels to be along the rows of an array.\n", "You should always have a look in the documentation!\n", "\n", "If you want to flip rows and columns, you can transpose the array:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b = a.T\n", "b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The transposed array is *not* a copy of the original one, it's rather a different *view* into the same memory.\n", "This means that if you change an element of the transposed array, this change will also be visible in the original array!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b[1, 2] = 0\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can see in the array properties that the transposed array doesn't \"own\" the data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a.flags.owndata" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b.flags.owndata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The transposed array has a property called `base` that holds a reference to the original array." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b.base is a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Create a two-channel signal with a sine tone in each channel.\n", "To do that, call `mysine()` twice and concatenate the two resulting arrays with `np.column_stack()`.\n", "Use different frequencies for the channels to be able to check which one is left and which is right.\n", "Listen to the signal with headphones and check which channel is left and which is right." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Array Properties\n", "\n", "Let's create some two-channel noise:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.random.normal(scale=0.2, size=(int(1.5 * fs), 2))\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Try those different ways to obtain the size of the array:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "len(x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.shape" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.size" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.nbytes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* What's the difference between them?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "len?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.ndarray.shape?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.ndarray.size?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.ndarray.nbytes?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* What's the length in seconds?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* There's much more information about the array, try the following commands and find out what they mean." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.ndim" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.dtype" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.itemsize" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.strides" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.flags" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:*\n", "You can also get some statistical values about the data in the array.\n", "Check if they are consistent with the given normally distributed noise signal." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.max()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.min()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.ptp()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.mean()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.std()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.var()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Most of these *methods* also exist as *functions*, e.g." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.max(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both the functions and the methods have an optional *axis* argument.\n", "\n", "*Exercise:* Try `axis=0` with all of the above functions/methods." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.std(axis=0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.mean(x, axis=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* What's the difference between `axis=0` and `axis=1`?\n", "What does `axis=-1` mean?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inter-aural Time Difference (ITD)\n", "\n", "You will learn about this later in the lecture, but you can already listen to it now!\n", "\n", "*Exercise:* Remember the array `t` of time instances from way before?\n", "Create a new two-dimensional array with `t` as its first column and `t` + 0.5 milliseconds as the second column.\n", "Use this two-channel array of time instances to create a two-channel sine tone with a frequency of 500 Hz.\n", "Both channels shall have the same frequency, but because of the delayed time instances, the right channel will be delayed." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Listen to the resulting two-channel signal using the function `myplay()` from above (use headphones!). What do you hear? What effect did the time-shift have?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you don't hear any difference, don't worry, just continue with the next exercise ...\n", "\n", "*Exercise:* Use two nested `for` loops to generate a sequence of two-channel signals and play them back immediately.\n", "Both channels should contain a sine tone with the same frequency and the same duration of half a second, but with a time lag relative to the other channel.\n", "In the inner loop, you should play back a series of signals with a relative delay of 0.6, 0.4, 0.2, 0, -0.2, -0.4 and -0.6 ms.\n", "The outer loop shall play this series three times, using the frequencies\n", "500, 1000 and 2000 Hz, respectively.\n", "Run the script and notice how you perceive (or not) the different time lags.\n", "This exercise is meant for headphone listening, do not use loudspeakers!\n", "Note that by default, [sd.play()](http://python-sounddevice.rtfd.org/#sounddevice.play) (and therefore also our little `myplay()` function) does not wait until playback is finished.\n", "But right now, we want it to.\n", "We can either use the `blocking=True` option, or we call [sd.wait()](http://python-sounddevice.rtfd.org/#sounddevice.wait) right after `myplay()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Broadcasting\n", "\n", "We already saw before, that when a scalar is multiplied by an array, this multiplication is done element-wise on the array.\n", "The NumPy people call this [broadcasting](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html).\n", "A detailed explanation can be found [here](https://jakevdp.github.io/PythonDataScienceHandbook/02.05-computation-on-arrays-broadcasting.html).\n", "\n", "The great thing about it is that it is not limited to operations between a scalar and an array, but also between arrays of different numbers of dimensions.\n", "\n", "Let's, for example, take a one-dimensional array with two values and multiply it with our two-dimensional array `x` from before:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.array([0.5, 100]) * x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although these two arrays clearly have a different shape and a different number of dimensions, the multiplication worked.\n", "In this case, each element of the first column of `x` was multiplied with the first value of the other array, and same for the second column and the second value of the array.\n", "\n", "In this example, the result has the same shape of one of the operands, and the other operand was \"stretched\" along its singular (or rather missing) dimension.\n", "\n", "But it doesn't have to be that way.\n", "Let's create a two-dimensional array that consists of only one column:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y = np.random.normal(scale=0.2, size=(1.5 * fs, 1))\n", "y" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we multiply a one-dimensional array with this two-dimensional column-array, none of the dimensions have the same size.\n", "Nevertheless, we can multiply them and both arrays get \"stretched\" (along their singular/missing dimension) leading to a result having a shape larger than any of the operands:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.array([0.5, 100]) * y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The left column of the result is `y` multiplied by the first element of the one-dimensional array, the right column is the same `y` multiplied by the second element.\n", "\n", "That's going to be useful in the next exercise." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Complex Harmonic Sounds\n", "\n", "*Exercise:* Create a 5-channel signal (i.e. a two-dimensional array with 5 columns) with a length of 2 seconds.\n", "Each channel shall contain a sine tone with its own frequency.\n", "Use the frequencies 200 Hz, 400 Hz, 600 Hz, 800 Hz and 1000 Hz.\n", "Use an amplitude of $A=0.3$ for the first channel, $A/2$ for the second channel, $A/3$ for the third etc.\n", "\n", "Hint: Do this by using a two-dimensional array with one column for the time instances and multiply this with a one-dimensional array of frequencies.\n", "The amplitudes should also be a one-dimensional array." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* From this 5-channel signal, create a mono signal, as a superposition of the five channels.\n", "Listen to it.\n", "\n", "Hint: Use [numpy.sum()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html) (or the equivalent method)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Same as above, but skip one of the frequencies.\n", "Try to hear the difference.\n", "Skip another one, etc." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Plot the mono signal(s). Do you recognize the sinusoidal components?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Add more tones (let's say a total of 20) using multiples of the fundamental frequency (with the same decreasing amplitudes as above).\n", "How does this sound like?\n", "How does the plot look like?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* How do things change if you only use the odd multiples of the fundamental frequency?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot a One-dimensional Plane Wave\n", "\n", "Consider a monochromatic (monofrequent) plane wave propagating along the $x$ axis,\n", "\n", "$$\n", "p(x, t) = \\hat{p} e^{j(k_x x - \\omega t)}\n", "$$\n", "\n", "where $\\hat{p}$ denotes the peak value of the sound pressure and $\\omega=2\\pi f$ the angular frequeqncy. The wave number in the $x$ direction is denoted by $k_x$ and related to $\\omega$ by the dispersion relation,\n", "\n", "$$\n", "k_x = \\frac{\\omega}{c}\n", "$$\n", "\n", "with $c$ denoting the speed of sound.\n", "\n", "In the following, we assume\n", "* $\\hat{p}=0.2$ Pa\n", "* $f=500$ Hz\n", "* $c=343$ m/s.\n", "\n", "*Exercise:* \n", "We placed a (pressure) microphone at $x=0$ m in order to observe the variation of the sound pressure over time. Plot the real part of the time-domain sound pressure. Check if the period of the time-domain signal is $\\frac{1}{f}$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Exercise:* Now plot the sound pressure between $x=0$ and $3$ m, for a fixed time instant $t=0$. Check if the wavelength is $\\frac{c}{f}$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Appendix\n", "\n", "If there is too much time left (which is unlikely), you can have a look at those commands:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%whos" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%qtconsole" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%time?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%timeit?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%profile?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Solutions\n", "\n", "If you had problems solving some of the exercises, don't despair!\n", "Have a look at the [example solutions](intro-solutions.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", " \n", " \"CC0\"\n", " \n", "
\n", " To the extent possible under law,\n", " the person who associated CC0\n", " with this work has waived all copyright and related or neighboring\n", " rights to this work.\n", "

" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python [default]", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.4" } }, "nbformat": 4, "nbformat_minor": 1 }