{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[exercises](intro.ipynb)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5])" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(6)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5]),\n", " array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5]))" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(0, 0.6, 0.1), np.arange(6) * 0.1 # two possibilities" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1]), '<-- wrong result!')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(0.5, 1.1, 0.1), \"<-- wrong result!\"" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 0.5, 0.6, 0.7, 0.8, 0.9, 1. ]), \"<-- that's right!\")" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(5, 11) * 0.1, \"<-- that's right!\"" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 1., 2., 3., 4., 5., 6.])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linspace(0, 6, 7)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 0., 1., 2., 3., 4., 5.]), array([ 0., 1., 2., 3., 4., 5.]))" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linspace(0, 6, 6, endpoint=False), np.linspace(0, 5, 6) # two possibilities" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5]),\n", " array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5]))" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linspace(0, 0.6, 6, endpoint=False), np.linspace(0, 0.5, 6) # again two possibilities" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 0.5, 0.6, 0.7, 0.8, 0.9, 1. ]),\n", " array([ 0.5, 0.6, 0.7, 0.8, 0.9, 1. ]))" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linspace(0.5, 1.1, 6, endpoint=False), np.linspace(0.5, 1, 6) # and again ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the number of elements is known and the step size should be obtained automatically $\\Rightarrow$ `np.linspace()` \n", "If the step size is known an if it's an integer and the number of elements should be obtained automatically $\\Rightarrow$ `np.arange()`\n", "\n", "If the step size is not an integer:\n", "\n", "* If the step size is a fraction of integers, you can use `np.arange()` with integers and divide the result accordingly.\n", "\n", "* If that's not feasible, calculate the expected number of elements beforehand and use `np.linspace()`" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dur, amp, freq, fs = 1, 0.3, 500, 44100\n", "t = np.arange(np.ceil(dur * fs)) / fs\n", "y = amp * np.sin(2 * np.pi * freq * t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "alternative (but inferior) methods to get $t$:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "t1 = np.arange(0, dur, 1/fs) # implicit rounding of dur!\n", "t2 = np.arange(0, np.round(dur), 1/fs) # still problematic: arange with floats\n", "# wrong if dur isn't an integer multiple of 1/fs:\n", "t3 = np.linspace(0, dur, np.round(dur * fs), endpoint=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Length of `y` must be *exactly* 44100 (using a half-open interval for $t$), not 44101 (which would be longer than 1 second).\n", "\n", "Plotting: 2 ways to zoom (there are probably more): draw a rectangle, drag with the right mouse button in pan/zoom mode.\n", "\n", "Clicks? Because of discontinuities (also in the derivatives) $\\Rightarrow$ Fade in/out! See [tools.fade()](tools.py)." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import sounddevice as sd\n", "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": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [], "source": [ "myplay(y)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def mysine(frequency, amplitude, duration):\n", " \"\"\"Generate sine tone with the given parameters @ 44.1 kHz.\"\"\"\n", " samplerate = 44100\n", " times = np.arange(np.ceil(duration * samplerate)) / samplerate\n", " return amplitude * np.sin(2 * np.pi * frequency * times)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [], "source": [ "z = mysine(440, 0.4, 3)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(z)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using matplotlib backend: TkAgg\n" ] } ], "source": [ "%matplotlib\n", "import matplotlib.pyplot as plt\n", "\n", "def myplot(data):\n", " \"\"\"Create a simple plot @ 44.1 kHz.\"\"\"\n", " samplerate = 44100\n", " times = np.arange(len(data)) / samplerate\n", " plt.plot(times, data)\n", " plt.xlabel(\"Time / Seconds\")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "myplot(mysine(440, 0.4, 3))" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import soundfile as sf\n", "\n", "dur, amp = 1, 0.3\n", "frequencies = 400, 500, 600 # Hz\n", "fadetime = 2000 # samples\n", "\n", "for freq in frequencies:\n", " sig = mysine(freq, amp, dur)\n", " sig = tools.fade(sig, fadetime)\n", " sf.write(\"sine_{}hz.wav\".format(freq), sig, 44100)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from scipy import signal\n", "\n", "f0, f1 = 100, 5000 # Hz\n", "amp = 0.2\n", "dur = 2 # seconds\n", "fadetime = 2000 # samples\n", "fs = 44100\n", "\n", "t = np.arange(np.ceil(dur * fs)) / fs\n", "\n", "for method in 'linear', 'log':\n", " sweep = amp * signal.chirp(t, f0, dur, f1, method)\n", " sweep = tools.fade(sweep, fadetime)\n", " sf.write('sweep_{}.wav'.format(method), sweep, fs)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": true }, "outputs": [], "source": [ "sinetone = mysine(frequency=500, amplitude=0.3, duration=1.5)\n", "noise = np.random.normal(scale=0.1, size=len(sinetone))\n", "sine_plus_noise = sinetone + noise" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(sine_plus_noise)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplot(sine_plus_noise)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dur = 2\n", "amp = 0.2\n", "\n", "two_sines = mysine(500, amp, dur) + mysine(507, amp, dur)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [], "source": [ "myplay(two_sines)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplot(two_sines)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Two sine tones with similar frequencies create \"beats\", see .\n", "The sum of these two tones is equivalent to an amplitude modulation with a carrier frequency of $\\frac{f_1+f_2}{2}$ and a modulation frequency of $\\frac{f_1-f_2}{2}$.\n", "\n", "$$\\cos(2\\pi f_1t)+\\cos(2\\pi f_2t) = 2\\cos\\left(2\\pi\\frac{f_1+f_2}{2}t\\right)\\cos\\left(2\\pi\\frac{f_1-f_2}{2}t\\right)$$\n", "\n", "We don't really *hear* the modulation frequency itself, we only hear the envelope of the modulation, therefore the *perceived* beat frequency is $f_{\\text{beat}} = f_1-f_2$." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": true }, "outputs": [], "source": [ "stereo_sines = np.column_stack([mysine(400, amp, dur), mysine(600, amp, dur)])" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(stereo_sines)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first column should be the left channel!" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": true }, "outputs": [], "source": [ "dur, amp = 1, 0.3\n", "freq = 500 # Hz\n", "delay = 0.5 # ms\n", "fs = 44100\n", "\n", "t = np.arange(np.ceil(dur * fs)) / fs\n", "times = np.column_stack((t, t - delay/1000))\n", "sig = amp * np.sin(2 * np.pi * freq * times)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(sig)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dur, amp = 0.5, 0.3\n", "frequencies = 500, 1000, 2000 # Hz\n", "delays = 0.6, 0.4, 0.2, 0, -0.2, -0.4, -0.6 # ms\n", "fs = 44100\n", "\n", "t = np.arange(np.ceil(dur * fs)) / fs\n", "\n", "for f in frequencies:\n", " for delay in delays:\n", " times = np.column_stack((t, t - delay/1000))\n", " sig = amp * np.sin(2 * np.pi * f * times)\n", " myplay(sig)\n", " sd.wait()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is supposed to illustrate [Lord Rayleigh's Duplex Theory](http://en.wikipedia.org/wiki/Interaural_time_difference#Duplex_theory) (at least the part about time differences)." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.00000000e+00],\n", " [ 2.26757370e-05],\n", " [ 4.53514739e-05],\n", " ..., \n", " [ 1.99993197e+00],\n", " [ 1.99995465e+00],\n", " [ 1.99997732e+00]])" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dur, amp = 2, 0.3\n", "frequencies = np.array([200, 400, 600, 800, 1000])\n", "fs = 44100\n", "t = np.arange(np.ceil(dur * fs)) / fs\n", "t.shape = -1, 1\n", "t" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.3 , 0.15 , 0.1 , 0.075, 0.06 ])" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "amplitudes = amp * 1 / np.arange(1, len(frequencies)+1)\n", "amplitudes" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(88200, 5)" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "five_sines = amplitudes * np.sin(2 * np.pi * frequencies * t)\n", "five_sines.shape" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": true }, "outputs": [], "source": [ "sum_of_sines = five_sines.sum(axis=1)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplot(sum_of_sines)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [], "source": [ "myplay(five_sines[:, [0, 1, 2, 3, 4]].sum(axis=1))" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [], "source": [ "myplay(five_sines[:, [0, 1, 2, 3]].sum(axis=1))" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(five_sines[:, [0, 1, 2, 4]].sum(axis=1))" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(five_sines[:, [0, 1, 3, 4]].sum(axis=1))" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(five_sines[:, [0, 2, 3, 4]].sum(axis=1))" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(five_sines[:, [1, 2, 3, 4]].sum(axis=1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000, 2200,\n", " 2400, 2600, 2800, 3000, 3200, 3400, 3600, 3800, 4000])" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f0 = 200 # Hz\n", "partials = 20\n", "\n", "frequencies = f0 * np.arange(1, partials + 1)\n", "frequencies" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.3 , 0.15 , 0.1 , 0.075 , 0.06 ,\n", " 0.05 , 0.04285714, 0.0375 , 0.03333333, 0.03 ,\n", " 0.02727273, 0.025 , 0.02307692, 0.02142857, 0.02 ,\n", " 0.01875 , 0.01764706, 0.01666667, 0.01578947, 0.015 ])" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "amplitudes = amp * 1 / np.arange(1, len(frequencies)+1)\n", "amplitudes" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(88200, 20)" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "many_sines = amplitudes * np.sin(2 * np.pi * frequencies * t)\n", "many_sines.shape" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "collapsed": true }, "outputs": [], "source": [ "sawtooth = many_sines.sum(axis=1)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplot(sawtooth)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplay(sawtooth)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://en.wikipedia.org/wiki/Sawtooth_wave" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "collapsed": true }, "outputs": [], "source": [ "square = many_sines[:, ::2].sum(axis=1)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "collapsed": true }, "outputs": [], "source": [ "myplot(square)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": false }, "outputs": [], "source": [ "myplay(square)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://en.wikipedia.org/wiki/Square_wave" ] }, { "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": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.3+" } }, "nbformat": 4, "nbformat_minor": 0 }