{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## ThinkDSP\n", "\n", "This notebook contains code examples from Chapter 6: Discrete Cosine Transform\n", "\n", "Copyright 2015 Allen Downey\n", "\n", "License: [Creative Commons Attribution 4.0 International](http://creativecommons.org/licenses/by/4.0/)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Get thinkdsp.py\n", "\n", "import os\n", "\n", "if not os.path.exists('thinkdsp.py'):\n", " !wget https://github.com/AllenDowney/ThinkDSP/raw/master/code/thinkdsp.py" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "PI2 = np.pi * 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Synthesis\n", "\n", "The simplest way to synthesize a mixture of sinusoids is to add up sinusoid signals and evaluate the sum." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from thinkdsp import CosSignal, SumSignal\n", "\n", "def synthesize1(amps, fs, ts):\n", " components = [CosSignal(freq, amp)\n", " for amp, freq in zip(amps, fs)]\n", " signal = SumSignal(*components)\n", "\n", " ys = signal.evaluate(ts)\n", " return ys" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's an example that's a mixture of 4 components." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from thinkdsp import Wave\n", "\n", "amps = np.array([0.6, 0.25, 0.1, 0.05])\n", "fs = [100, 200, 300, 400]\n", "framerate = 11025\n", "\n", "ts = np.linspace(0, 1, framerate, endpoint=False)\n", "ys = synthesize1(amps, fs, ts)\n", "wave = Wave(ys, ts, framerate)\n", "wave.apodize()\n", "wave.make_audio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can express the same process using matrix multiplication." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def synthesize2(amps, fs, ts):\n", " args = np.outer(ts, fs)\n", " M = np.cos(PI2 * args)\n", " ys = np.dot(M, amps)\n", " return ys" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And it should sound the same." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ys = synthesize2(amps, fs, ts)\n", "wave = Wave(ys, framerate)\n", "wave.apodize()\n", "wave.make_audio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can confirm that the differences are small." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.2789769243681803e-13" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ys1 = synthesize1(amps, fs, ts)\n", "ys2 = synthesize2(amps, fs, ts)\n", "np.max(np.abs(ys1 - ys2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Analysis\n", "\n", "The simplest way to analyze a signal---that is, find the amplitude for each component---is to create the same matrix we used for synthesis and then solve the system of linear equations." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def analyze1(ys, fs, ts):\n", " args = np.outer(ts, fs)\n", " M = np.cos(PI2 * args)\n", " amps = np.linalg.solve(M, ys)\n", " return amps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the first 4 values from the wave array, we can recover the amplitudes." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.6 , 0.25, 0.1 , 0.05])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n = len(fs)\n", "amps2 = analyze1(ys[:n], fs, ts[:n])\n", "amps2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What we have so far is a simple version of a discrete cosine tranform (DCT), but it is not an efficient implementation because the matrix we get is not orthogonal." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# suppress scientific notation for small numbers\n", "np.set_printoptions(precision=3, suppress=True)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1. , 1. , 1. , 1. ],\n", " [ 1. , 0.707, 0. , -0.707],\n", " [ 1. , 0. , -1. , -0. ],\n", " [ 1. , -0.707, -0. , 0.707]])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def test1():\n", " N = 4.0\n", " time_unit = 0.001\n", " ts = np.arange(N) / N * time_unit\n", " max_freq = N / time_unit / 2\n", " fs = np.arange(N) / N * max_freq\n", " args = np.outer(ts, fs)\n", " M = np.cos(PI2 * args)\n", " return M\n", "\n", "M = test1()\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To check whether a matrix is orthogonal, we can compute $M^T M$, which should be the identity matrix:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 4., 1., -0., 1.],\n", " [ 1., 2., 1., 0.],\n", " [-0., 1., 2., 1.],\n", " [ 1., 0., 1., 2.]])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.transpose().dot(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But it's not, which means that this choice of M is not orthogonal.\n", "\n", "Solving a linear system with a general matrix (that is, one that does not have nice properties like orthogonality) takes time proportional to $N^3$. With an orthogonal matrix, we can get that down to $N^2$. Here's how:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0.981, 0.831, 0.556, 0.195],\n", " [ 0.831, -0.195, -0.981, -0.556],\n", " [ 0.556, -0.981, 0.195, 0.831],\n", " [ 0.195, -0.556, 0.831, -0.981]])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def test2():\n", " N = 4.0\n", " ts = (0.5 + np.arange(N)) / N\n", " fs = (0.5 + np.arange(N)) / 2\n", " args = np.outer(ts, fs)\n", " M = np.cos(PI2 * args)\n", " return M\n", " \n", "M = test2()\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now $M^T M$ is $2I$ (approximately), so M is orthogonal except for a factor of two." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 2., -0., 0., 0.],\n", " [-0., 2., -0., -0.],\n", " [ 0., -0., 2., -0.],\n", " [ 0., -0., -0., 2.]])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.transpose().dot(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And that means we can solve the analysis problem using matrix multiplication." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def analyze2(ys, fs, ts):\n", " args = np.outer(ts, fs)\n", " M = np.cos(PI2 * args)\n", " amps = M.dot(ys) / 2\n", " return amps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It works:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.6 , 0.25, 0.1 , 0.05])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n = len(fs)\n", "amps2 = analyze1(ys[:n], fs, ts[:n])\n", "amps2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### DCT\n", "\n", "What we've implemented is DCT-IV, which is one of several versions of DCT using orthogonal matrices." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def dct_iv(ys):\n", " N = len(ys)\n", " ts = (0.5 + np.arange(N)) / N\n", " fs = (0.5 + np.arange(N)) / 2\n", " args = np.outer(ts, fs)\n", " M = np.cos(PI2 * args)\n", " amps = np.dot(M, ys) / 2\n", " return amps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can check that it works:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8.326672684688674e-17" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "amps = np.array([0.6, 0.25, 0.1, 0.05])\n", "N = 4.0\n", "ts = (0.5 + np.arange(N)) / N\n", "fs = (0.5 + np.arange(N)) / 2\n", "ys = synthesize2(amps, fs, ts)\n", "\n", "amps2 = dct_iv(ys)\n", "np.max(np.abs(amps - amps2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "DCT and inverse DCT are the same thing except for a factor of 2." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "def inverse_dct_iv(amps):\n", " return dct_iv(amps) * 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And it works:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8.326672684688674e-17" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "amps = [0.6, 0.25, 0.1, 0.05]\n", "ys = inverse_dct_iv(amps)\n", "amps2 = dct_iv(ys)\n", "np.max(np.abs(amps - amps2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dct\n", "\n", "`thinkdsp` provides a `Dct` class that encapsulates the DCT in the same way the Spectrum class encapsulates the FFT." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from thinkdsp import TriangleSignal\n", "\n", "signal = TriangleSignal(freq=400)\n", "wave = signal.make_wave(duration=1.0, framerate=10000)\n", "wave.make_audio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To make a Dct object, you can invoke `make_dct` on a Wave." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "from thinkdsp import decorate\n", "\n", "dct = wave.make_dct()\n", "dct.plot()\n", "decorate(xlabel='Frequency (Hz)', ylabel='DCT')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dct provides `make_wave`, which performs the inverse DCT." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "wave2 = dct.make_wave()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result is very close to the wave we started with." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7.771561172376096e-16" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.max(np.abs(wave.ys-wave2.ys))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Negating the signal changes the sign of the DCT." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "signal = TriangleSignal(freq=400, offset=0)\n", "wave = signal.make_wave(duration=1.0, framerate=10000)\n", "wave.ys *= -1\n", "wave.make_dct().plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Adding phase offset $\\phi=\\pi$ has the same effect." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYYAAAD4CAYAAADo30HgAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/d3fzzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAVGElEQVR4nO3df7Bc5X3f8ff33ouEBIhfwggkYclG2BUEO+ZGxUnqBIODiD2GzNgdxZNBM2WqKYNbO54mhdDJNNNhEly31DS1p2rwGJy4mNhx0JBQG0p+dMYgcgk/BZZ9McQIyUaUH1ZNJZD07R/7XGmfq7260l2du3fvfb9mVvfsc87Z83x3V/vZ55yzu5GZSJI0ZqDXHZAkzSwGgySpYjBIkioGgySpYjBIkipDve5AtxYvXpwrVqzodTckqa888sgjL2fmGZ3m9X0wrFixgpGRkV53Q5L6SkT8w0Tz3JUkSaoYDJKkisEgSaoYDJKkisEgSaoYDJKkisEgSaoYDA2654ntvPbGm73uhiQdFYOhIdtefYNPfvVRPvnVR3vdFUk6KgZDQ/bs3Q/A9tf+X497IklHx2BomL+PJ6nfGAwNifLXn06V1G8MhoZExOQLSdIMZDA0zPGCpH5jMDTE8YKkfmUwNMxDDJL6jcHQEA8xSOpXBkPDfvjKG73uwpTt3LWH//jtrezf77BHmksMhoa8ta//X0yv/8YT/JcHRtn83Cu97oqkaWQwaEJv7mt9envP3n097snUfWf0Ze5+7MVed0PqKzMuGCJibURsjYjRiLi+1/2Zuv4fMQwNtA6U7OvjXUmf+KPNfOrOx3rdja78/l8+w3eefbnX3ejKcy//tNdd0FGYUcEQEYPAfwWuAFYDvx4Rq3vbq6lpPxvplZ/25zes/nRPa6Sw/fXdPe7JzJWZjX+D7n/72x/wif++ubHbf+2NN/nh/2nuWNj/fGoHl3zur7nv6R83to3vPPsyLzR4PO+OB5/nis//78Zuf/db+/j39zzN62+81dg2jkbMpK9siIj3A/8uMy8v128AyMzfn2id4eHhHBkZOept3f3Yi3z+/u8zb2jgkFNKIw6+sCdZzd+fSQI/2Dn1d0DzhwY446T5DA0EAwPBQAQDAVE+/dB+RlNEkJmHfJJ6aCB48sXXAVh91qIp92UiT+/4ySFty09bwHGDAwxGq8/j+zre4T79HbTuy+/+aBfQfQ3jN7U/4ZlxNbxj8QkMDgSDA4fp17gbmuzksrH7ad7QAO8848SuP7/SvvnM+nF4++kLmT800Hbfx4GvXDmafkfAQAQRreUe39Z6Hr17yUmH3NZU6hn/WGzZfrCG88488UD/O/V7KttNDj7W715yUsfb7LaOsRrOXDSfUxfOa1vm8M/xI91O+3009jhMdPvtLdddci4fvvCsI9zS+O3GI5k53Gne0JRusTlLgRfarm8D/vH4hSJiA7AB4JxzzpnShuYPDbLstIW8tXc/WXb7tAdAln/GgiEpoZDdf//R0lMWsGLxCQRjT76D/0kPbHtsOuugGnue7C37/wHOPmVBV/3ppFMwvPOMExkaCCLisH1lXBsc+oIHsGv3wXdHh68had07ne/3gyHeemz2Z+fdX+8444RJv6qkvb+TP8x54H5afuoClh7R49Bey6E1tdeyd3/CjoPzFp84n9NPmHdgjfb7+8j7fejzecyyUxceWP/I6m+viQP1tNewf9yNvP30Ew5du217R7fdg9sfC4blpy3s8vYOLjy23v5MtpS2UxbMY/lpCw9Zpn177e0Tb+PgY59ZB8NEt9/pdk+YPzh5SVMw04Kh0//aQ+7izNwIbITWiGEqG1p7wRLWXrBkKqsekRXX/8WB6ef/4MONbGNsv+3KxYf+ZzsWxmq47zc/wKozT5pk6al5/IXXWHXmiSyc18xTcayGph6D/fuTrT/exT9qYNQG8Obe/Zz3b+8F4BvX/nwj29j91j7e2refk44/rpHbv+eJ7Xzyq4/yrU9/gHctaeZ59PT2n7D01AWcvKCZGjY9vp2tP/oJv3X5uxu5/T179/HHD/2Q9e9/O0ODvd/DP9OCYRuwvO36MmB7j/rSlV867wz+5ns7G91GU4Ew5uyTj2f767sb/bDee5af0tyNT4OBgWgsFACOG2zd+Vc0+Cbm+OMGOf64Zt55AnzkwrO55F1v44T5zb3crD67uccA4KPvORvec3Zjtz9/aJBrfnFlY7d/tGZaMPwdsCoiVgIvAuuAT/S2S1OzcF5z/9Gmy4JSwww6DDXnRASbf+dSTlnYzDvh6dJkKOjYm1GPVmbujYhPAt8CBoEvZeaWSVabkU46fkbdtVPiV4fPDGcuOr7XXdAc0/udWeNk5l9m5nmZ+c7MvKnX/ZmqX/vZZUAzZwxNNwcM0tzS/29rZ6ihsm+4n3cpzYbxwm9d/q6+/RyJ1CsGQ8N8t91b111ybq+7IPWdGbcrabaYDe+2x3jwWZpbDAZN6MCHahz3SHOKwdCwmfSVI0crZtW4R9KRMhgaMpvO9OzjbJM0BQZDw/r5NXU2hZukI2cwNMZXVUn9yWBo2GzYDTMbapB05AyGhsym3TCelSTNLQaDJuR3JUlzk8GgSbkrSZpbDAZNyPGCNDcZDJrQZz50HvMGBxr/QSBJM4tfoqcJXbb6TL530xW97oakaeaIQZJUMRgkSRWDQZJUMRgkSRWDQZJUMRgkSRWDQZJUMRgkSRWDQZJUMRgkSRWDQZJUMRgkSRWDQZJUMRga4o/bSOpXBoMkqWIwNMSfS5bUrwwGSVLFYJAkVQwGSVLFYJAkVQwGSVKlq2CIiP8QEd+NiCci4psRcUrbvBsiYjQitkbE5W3tF0XEk2XerRGt83ciYn5EfK20b46IFd30TZI0Nd2OGO4DLsjMC4HvATcARMRqYB1wPrAW+EJEDJZ1vghsAFaVy9rSfg3wamaeC9wC3Nxl3yRJU9BVMGTmtzNzb7n6ELCsTF8J3JmZezLzOWAUWBMRZwGLMvPBzEzgDuCqtnVuL9NfBy4dG01IkqbPsTzG8M+Ae8v0UuCFtnnbStvSMj2+vVqnhM3rwOmdNhQRGyJiJCJGdu7cecwKkCTB0GQLRMT9wJIOs27MzLvLMjcCe4E/GVutw/J5mPbDrXNoY+ZGYCPA8PCw30okScfQpMGQmZcdbn5ErAc+Alxadg9BaySwvG2xZcD20r6sQ3v7OtsiYgg4GXjlCGqQJB1D3Z6VtBb4N8BHM/ONtlmbgHXlTKOVtA4yP5yZO4BdEXFxOX5wNXB32zrry/THgAfagkaSNE0mHTFM4g+B+cB95TjxQ5n5LzJzS0TcBTxNaxfTdZm5r6xzLfBlYAGtYxJjxyVuA74SEaO0RgrruuybJGkKugqGcmrpRPNuAm7q0D4CXNChfTfw8W76I0nqnp98liRVDAZJUsVgkCRVDAZJUsVgkCRVDIaG+UEMSf3GYGiI3/4nqV8ZDJKkisHQEHchSepXBkPD3KUkqd8YDJKkisEgSaoYDJKkisEgSaoYDJKkisEgSaoYDJKkisEgSaoYDJKkisEgSaoYDJKkisEgSaoYDJKkisEgSaoYDJKkisEgSaoYDA3zl9wk9RuDoSH+cpukfmUwSJIqBoMkqWIwSJIqBoMkqWIwSJIqBkNDPE1VUr8yGBrmaauS+o3BIEmqGAySpMoxCYaI+NcRkRGxuK3thogYjYitEXF5W/tFEfFkmXdrRERpnx8RXyvtmyNixbHomyTp6HQdDBGxHPgQ8MO2ttXAOuB8YC3whYgYLLO/CGwAVpXL2tJ+DfBqZp4L3ALc3G3fJElH71iMGG4Bfpv6RJwrgTszc09mPgeMAmsi4ixgUWY+mJkJ3AFc1bbO7WX668ClY6MJSdL06SoYIuKjwIuZ+fi4WUuBF9qubyttS8v0+PZqnczcC7wOnD7BdjdExEhEjOzcubObEiRJ4wxNtkBE3A8s6TDrRuB3gF/ptFqHtjxM++HWObQxcyOwEWB4eNiPDEjSMTRpMGTmZZ3aI+JngJXA42WPzzLg7yNiDa2RwPK2xZcB20v7sg7ttK2zLSKGgJOBV46mGElS96a8Kykzn8zMt2XmisxcQeuF/X2Z+SNgE7CunGm0ktZB5oczcwewKyIuLscPrgbuLje5CVhfpj8GPFCOQ0iSptGkI4apyMwtEXEX8DSwF7guM/eV2dcCXwYWAPeWC8BtwFciYpTWSGFdE32TJB3eMQuGMmpov34TcFOH5UaACzq07wY+fqz6I0maGj/5LEmqGAwN8yCJpH5jMDTET+ZJ6lcGgySpYjBIkioGgySpYjBIkioGgySpYjBIkioGgySpYjBIkioGQ0P8xLOkfmUwNMxPQEvqNwaDJKliMEiSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMDTMr8aQ1G8Mhob4VRiS+pXBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpErXwRAR/zIitkbEloj4bFv7DRExWuZd3tZ+UUQ8WebdGhFR2udHxNdK++aIWNFt33rJb1WV1K+6CoaIuAS4ErgwM88HPlfaVwPrgPOBtcAXImKwrPZFYAOwqlzWlvZrgFcz81zgFuDmbvo2U/gtq5L6TbcjhmuBP8jMPQCZ+VJpvxK4MzP3ZOZzwCiwJiLOAhZl5oOZmcAdwFVt69xepr8OXDo2mpAkTZ9ug+E84J+UXT9/ExE/V9qXAi+0LbettC0t0+Pbq3Uycy/wOnB6p41GxIaIGImIkZ07d3ZZgiSp3dBkC0TE/cCSDrNuLOufClwM/BxwV0S8g857UPIw7Uwyr27M3AhsBBgeHnZ3viQdQ5MGQ2ZeNtG8iLgW+LOyW+jhiNgPLKY1EljetugyYHtpX9ahnbZ1tkXEEHAy8MqRlyJJOha63ZX058AHASLiPGAe8DKwCVhXzjRaSesg88OZuQPYFREXl+MHVwN3l9vaBKwv0x8DHiiBI0maRpOOGCbxJeBLEfEU8CawvryYb4mIu4Cngb3AdZm5r6xzLfBlYAFwb7kA3AZ8JSJGaY0U1nXZtxnBZJPUb7oKhsx8E/iNCebdBNzUoX0EuKBD+27g4930ZybxdCpJ/cpPPkuSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMEiSKgaDJKliMDTEX26T1K8Mhob5S26S+o3BIEmqGAwNc5eSpH5jMDTEXUiS+pXBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqGAySpIrBIEmqdBUMEfHeiHgoIh6LiJGIWNM274aIGI2IrRFxeVv7RRHxZJl3a0REaZ8fEV8r7ZsjYkU3fZMkTU23I4bPAr+Xme8FfrdcJyJWA+uA84G1wBciYrCs80VgA7CqXNaW9muAVzPzXOAW4OYu+yZJmoJugyGBRWX6ZGB7mb4SuDMz92Tmc8AosCYizgIWZeaDmZnAHcBVbevcXqa/Dlw6NpqQJE2foS7X/zTwrYj4HK2Q+fnSvhR4qG25baXtrTI9vn1snRcAMnNvRLwOnA68PH6jEbGB1qiDc845p8sSJEntJg2GiLgfWNJh1o3ApcBvZuY3IuKfArcBl9H5J4/zMO1MMq9uzNwIbAQYHh7uuEyvDQ60ypk/5PF9Sf1l0mDIzMsmmhcRdwCfKlf/FPijMr0NWN626DJau5m2lenx7e3rbIuIIVq7pl6ZvISZ6WeWnsy/unQVn1jjiEZSf+n27ex24JfK9AeB75fpTcC6cqbRSloHmR/OzB3Aroi4uBw/uBq4u22d9WX6Y8AD5ThEX4oIPvOh81hy8vG97ookHZVujzH8c+Dz5R3+bsp+/8zcEhF3AU8De4HrMnNfWeda4MvAAuDecoHWbqivRMQorZHCui77JkmagujjN+VA6xjDyMhIr7shSX0lIh7JzOFO8zwyKkmqGAySpIrBIEmqGAySpIrBIEmqGAySpErfn64aETuBf5ji6ovp8F1Ms5w1zw3WPDd0U/PbM/OMTjP6Phi6EREjE53HO1tZ89xgzXNDUzW7K0mSVDEYJEmVuR4MG3vdgR6w5rnBmueGRmqe08cYJEmHmusjBknSOAaDJKkyZ4MhItZGxNaIGI2I63vdn25ExJci4qWIeKqt7bSIuC8ivl/+nto274ZS99aIuLyt/aKIeLLMu7X8mNKMExHLI+KvIuKZiNgSEZ8q7bO55uMj4uGIeLzU/HulfdbWDBARgxHxaETcU67P6noBIuL50t/HImKktE1v3Zk55y7AIPAs8A5gHvA4sLrX/eqing8A7wOeamv7LHB9mb4euLlMry71zgdWlvthsMx7GHg/rd/fvhe4ote1TVDvWcD7yvRJwPdKXbO55gBOLNPHAZuBi2dzzaWvnwG+Ctwz25/XbTU/Dywe1zatdc/VEcMaYDQzf5CZbwJ3Alf2uE9Tlpl/y6G/j30lcHuZvh24qq39zszck5nPAaPAmog4C1iUmQ9m61l1R9s6M0pm7sjMvy/Tu4BngKXM7pozM/9vuXpcuSSzuOaIWAZ8mIO/JQ+zuN5JTGvdczUYlgIvtF3fVtpmkzOz9RvblL9vK+0T1b60TI9vn9EiYgXws7TeQc/qmstulceAl4D7MnO21/yfgd8G9re1zeZ6xyTw7Yh4JCI2lLZprbvb33zuV532tc2V83Ynqr3v7pOIOBH4BvDpzPzJYXahzoqas/W76e+NiFOAb0bEBYdZvK9rjoiPAC9l5iMR8ctHskqHtr6pd5xfyMztEfE24L6I+O5hlm2k7rk6YtgGLG+7vgzY3qO+NOXHZThJ+ftSaZ+o9m1lenz7jBQRx9EKhT/JzD8rzbO65jGZ+Rrw18BaZm/NvwB8NCKep7Wr94MR8cfM3noPyMzt5e9LwDdp7fqe1rrnajD8HbAqIlZGxDxgHbCpx3061jYB68v0euDutvZ1ETE/IlYCq4CHy/B0V0RcXM5euLptnRml9O824JnM/E9ts2ZzzWeUkQIRsQC4DPgus7TmzLwhM5dl5gpa/z8fyMzfYJbWOyYiToiIk8amgV8BnmK66+71EfheXYBfpXU2y7PAjb3uT5e1/A9gB/AWrXcK1wCnA/8L+H75e1rb8jeWurfSdqYCMFyehM8Cf0j5ZPxMuwC/SGtY/ATwWLn86iyv+ULg0VLzU8DvlvZZW3Nbf3+Zg2clzep6aZ0p+Xi5bBl7bZruuv1KDElSZa7uSpIkTcBgkCRVDAZJUsVgkCRVDAZJUsVgkCRVDAZJUuX/A81bb8mcVexNAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "signal = TriangleSignal(freq=400, offset=np.pi)\n", "wave = signal.make_wave(duration=1.0, framerate=10000)\n", "wave.make_dct().plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [] } ], "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.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }