{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Transforming Lapped Signal Vectors\n",
    "\n",
    "[Nils Werner](https://www.audiolabs-erlangen.de/fau/assistant/werner) and [Bernd Edler](https://www.audiolabs-erlangen.de/fau/professor/edler)\n",
    "\n",
    "[International Audio Laboratories Erlangen](https://www.audiolabs-erlangen.de/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import scipy as sp\n",
    "import scipy.signal as ss\n",
    "import scipy.linalg\n",
    "import skimage.util\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.patches as patches\n",
    "import itertools\n",
    "\n",
    "from utils import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the previous Notebook we briefly investigated lapped transforms by statically analysing their properties.\n",
    "\n",
    "Crucially, we found a matrix that lets us investigate how two frames will interact when analyzed using MDCT.\n",
    "\n",
    "In this Notebook we will see how we can use the introduced matrices to actually transform signals.\n",
    "\n",
    "# Lapped Signal Vectors\n",
    "\n",
    "The first step is to create a view of our data that can directly be transformed by our two-frame analysis matrix.\n",
    "\n",
    "Because we are working with lapped transforms, this view needs to be lapped. In essence we are trying to implement\n",
    "\n",
    "$$\n",
    "\\mathbf{X}_{n,k} = \\vec{x}_{nN+k} \\qquad k = 0, \\dots, LN\n",
    "$$\n",
    "\n",
    "Where $N$ is the framelength and $L$ is the overlap factor (in this case $L=2$). This can efficiently be implemented by modifying the \"stride of an array\", and for NumPy two functions are available:\n",
    "\n",
    "```python\n",
    "skimage.util.view_as_windows()\n",
    "```\n",
    "\n",
    "and\n",
    "\n",
    "```python\n",
    "numpy.lib.stride_tricks.as_strided()\n",
    "```\n",
    "\n",
    "However for convenience we have created several utility functions `lap()`, `unlap()` and others. Please inspect `utils.py` for details.\n",
    "\n",
    "First we will play around with our `lap()` and `unlap()` functions to get a feel for what they do."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x86.4 with 5 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "N = 4\n",
    "L = 2\n",
    "\n",
    "# create input signal\n",
    "x = np.sin(2 * np.pi * np.arange(16) / 44100 * 4400)\n",
    "\n",
    "# cut signal into frames of length N\n",
    "x = x.reshape(-1, N)\n",
    "\n",
    "# create lapped view of x\n",
    "x_lapped = lap(x, L)\n",
    "\n",
    "# reconstruct framed signal from lapped view\n",
    "x_out = unlap(x_lapped, L)\n",
    "\n",
    "# Plot data\n",
    "f, (a, b, c, d, e) = plt.subplots(1, 5, figsize=(18, 1.2))\n",
    "a.imshow(x.ravel()[None, :])\n",
    "a.set_title(\"flattened x\")\n",
    "b.imshow(x)\n",
    "b.set_title(\"x\")\n",
    "c.imshow(x_lapped)\n",
    "c.set_title(\"x_lapped\")\n",
    "d.imshow(x_out)\n",
    "d.set_title(\"x_out\")\n",
    "e.imshow(x_out.ravel()[None, :])\n",
    "e.set_title(\"flattened x_out\")\n",
    "\n",
    "a.add_patch(patches.Rectangle((3.5, -0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "b.add_patch(patches.Rectangle((-0.5, 0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "c.add_patch(patches.Rectangle((3.5, -0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "c.add_patch(patches.Rectangle((-0.5, 0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "d.add_patch(patches.Rectangle((-0.5, 0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "e.add_patch(patches.Rectangle((3.5, -0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "\n",
    "c.annotate('same data', xy=(2, 1), xytext=(5, 2), color='black', arrowprops={'facecolor': 'white', 'shrink': 0.05})\n",
    "c.annotate('', xy=(6, 0), xytext=(5, 1.8), arrowprops={'facecolor': 'white', 'shrink': 0.05})\n",
    "\n",
    "None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can clearly see the right part of each frame in `x_lapped` repeating in the left half of the following frame. In other words: this representation is lapped by exactly 50% (`L=2`).\n",
    "\n",
    "If we were to modify any element in `x_lapped`, the corresponding element in the lapped neighbor would change, too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABB0AAABwCAYAAABSIAgJAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzt3Xl8FdX5x/HPQwKBhLCDsgm4VgEFClFEKooKotVWrUvVulRta7Fq0brQX13qvlVbd3Gt1qXVWutGFRcUUIgIWMGFKsqqrEICBEOe3x8ziZeQkJvcZXJvvu/Xixd3Zs6Z+wzMvXPPM2fOMXdHRERERERERCTZmkUdgIiIiIiIiIhkJyUdRERERERERCQllHQQERERERERkZRQ0kFEREREREREUkJJBxERERERERFJCSUdRERERERERCQllHQQERGRejGzN8zsjKjjqM7M3Mx2jjoOERER+Y6SDiIiIiIRMjOLOgYREZFUUdJBREREJCJKOEgqmNkCMzso6jhEREBJBxGROpnZTma2yswGhcvdzGy5mY2IODSRhCV6fof1XzOzlWa2wsweM7N2MdsXmNklZjbXzFab2YNm1jLcNsLMFpnZpWHdBWZ2YkzdPDO7ycy+NLOvzOxuM2sVs/1CM1tqZkvM7PSk/aOkkYeijkNERCRVlHQQEamDu/8PuAh41MzygQeBh939jUgDE0mCJJzfBlwLdAN2B3oCl1crcyIwCtgJ2BX4fcy27YFOQHfgFOBeM9st3HZdWH4AsHNY5g8AZjYauAA4GNgF0F1dabQaktwLyzwX1ptvZmfGbHvIzK6KWR5hZovC138FdgD+bWYlZva7lB2YiEgclHQQEYmDu98HzAfeBboC46ONSCR5Ejm/3X2+u7/i7mXuvhy4Bdi/WrHb3X2hu68CrgZOqLb9/8L6bwIvAMeGjx2cBZzv7qvcfR1wDXB8WOdY4EF3/6+7l7J1okOk0Whgcu8JYBFBQu8Y4BozOzCO9zoZ+BL4obu3dvcbEo1fRCQRuVEHICKSQe4DngPOcveyqIMRSbIGnd9mth1wGzAcKCS4obG6WrGFMa+/IGhEVVodJg2qb+8M5APvxQx7YEBO+Lob8F61eiKNlrvfZ2Y/JEjuOXBEbWXNrCcwDDjM3TcCs8xsAvAz4LV0xCsikizq6SAiEgczaw3cCtwPXG5mHSIOSSRpEjy/ryFoQPV39zbASQTJgVg9Y17vACyJWW5vZgU1bF8BbAD6unu78E9bd28dlltaw35FGrv7gH7AX+pI7nUDKnv4VPqC4BEjEZGMoqSDiEh8bgOK3f0Mgu7fd0ccj0gyJXJ+FwIlwDdm1h24sIYyvzazHmEyYzzwZLXtV5hZCzMbDhwO/N3dKwgaaH8ysy4AZtbdzEaFdZ4CTjWzPcLu6pfVI2aRtKtncm8J0MHMCmPW7QAsDl+XEvQEqrR9tfoanFREGg0lHURE6mBmRwKjgV+Fq34LDIodZV8kUyXh/L4CGAR8Q5CweKaGMn8D/gN8BvwPuCpm2zKCxzGWAI8Bv3T3j8JtFxGMNfGOma0FXgV2A3D3lwgacK+FZdTlXBq7uJN77r4QmApca2YtzWxP4OfAo2GRWcAYM+tgZtsD51XbxVfAjsk+ABGRhjDN0iQiIiKpYmYLgDPc/dUato0AHnX3HumOKypmZpois+kJk3t3EjyGtCrs9TALuMzdH6ulTg+CxMS+BIm5G9397nBbS+Bh4FBgAcHAlOMqP0vh+/0FaANc5e43pfDwRES2SUkHERERSRklHb4TzsiBkg4iItKUaPYKERER2YqZldSy6VB3fyutwWQJJRtERKQpUk8HEREREZEIKLknIk2Bkg4iIiIiIiIikhKavUJEREREREREUkJjOohIxshpXeC57bc1rfm2dWyzLuEYuuVuTHgfiViw8FtWrNpskQaR5RI9z2qTjPOvuqjPx7jNKYs6gqw2hxZs8jJ9L6SQrj+6/ohIwynpICIZI7d9B7qNqz4Vefx+NnJywjFc1nluwvtIRNGohZG+f1OQ6HlWm2Scf9VFfT7Gq1nX+VGHkNXaUBB1CFlP1x9df0Sk4fR4hYiIiIiIiIikhHo6iIiISNpd+cHhSdvXg5NGJG1flT4/d1zC+3B3zLbsjX5ws58kvN9Kr/g/krYvERGRVFFPBxGJlJmNNrOPzWy+mV0cdTwiIsmg2cEaN117RETSR0kHEYmMmeUAdwCHAnsAJ5jZHtFGJSKSODPbqpeDNA669oiIpJeSDiISpSJgvrt/5u6bgCeAIyOOSUREspuuPSIiaaSkg4hEqTsQOxz2onCdSNKoG7WIVKNrj4hIGinpICKNmpmdZWbFZla8ubQ06nAkw6gbtYg0lK4/IiLJoaSDiERpMdAzZrlHuK6Ku9/r7oPdfXBOgeail3pTN2oRqa7Oaw/o+iMikixKOohIlGYAu5hZHzNrARwPPBdxTJJd1I1aRKrTtUdEJI2UdIiAme1mZrPMbJ2Z/cbMHjKzq6KOqyEyOXaJnruXA2OBicA84Cl3/zDaqKQpUjdqkaZD1x4RkfRS0iEavwNed/dCd/9zfSqa2Rtmdka1dW5mOyc1QpE0cfcX3X1Xd9/J3a+OOh7JOupGLSnx7LPP0rt3b7788ksA3D3iiKQ+dO0RaTjdQJX6UtIhGr0AZdRFRFJP3aglqdasWcMpp5zCBRdcwOrVq9m0aZMSDiLS1OgGagqY2alm9nbUcaRCbtQBNDVm9hqwP7Cfmd0KDKq2vT3wV2Bvgv+fKcAv3X2RmV0NDAf2Ces+BOwZVp1tZg783N2fNLPDgauA3sDccB9zwvdYANwO/IwgAfIycIq7bwy3b6vuQOB+YBfgRaDWX1pmdhfQxd2PDpevBwYDB7l+oUkD9F+0iOLzL4g6jGjtmRd1BBnF3cvNrLIbdQ7wgLpRS0O9+uqrnH766Rx++OHMmjWLnXfemYKCAsws6tAkxXT9QdcfidWLYGBmkbiop0OaufuBwFvAWHdv7e6fVCvSDHiQ4MO8A7CBIEGAu4+vVnesu/8grLdXuO7JMDHwAPALoCNwD/CcmcVeLY4FRgN9CBIXp0JVUqHGuuFdwmcJkiIdgL8DR2/jcMcB/cOs3XDg5wTJDSUcRBrokzn5UYeQcdSNWhK1fv16zjnnHE477TQmTJjAnXfeSevWrSkpKaFAj+NIE6Hrj0DVDdQDgNvNrMTMdq22vb2ZPW9my81sdfi6R7it8gZqZd3bzWxyWHV2uO64sOzh4SMca8xsqpntGfMeC8zsAjObY2bfmNmTZtYyZvu26g40s5nhoyFPAlX1ajjWu8zs6Zjl681sktWRaTazM81svpmtMrPnzKxbuL532KsjN6bsG2Z2hpntDtwNDA3/HdZs6z0yjZIOjYy7r3T3p919vbuvA64m6BlRH2cB97j7u+6+2d0fBsqAfWLK/Nndl7j7KuDfwIA46u4DNAdudfdv3f0fBF2XazuW9cDJwC3Ao8A57r6onsciIiISmWnTpjFgwABWr17NnDlzOOSQQ4BgDIf169cr6SAiTYpuoG77BqqZHQhcG8bXFfiCOHqFuPs84JfAtPDfoV1ddTKJkg6NjJnlm9k9ZvaFma0FJgPtzCynHrvpBYwLs3trwkxZT6BbTJllMa/XA63jqNsNWFztg/bFtgJx93eBzwADnqrHMYiIiESmrKyMSy+9lB//+Mdce+21PProo7Rv375q+4YNG8jLyyMnpz6XZxGR7KYbqJxI8CjnTHcvAy4h6L3Quz7/ANlGSYfGZxywG7C3u7cBKrN/ld144nk0YSFwtbu3i/mT7+6PJ1h3KdC9WpeiHba1MzP7NZAHLCEYdEZERKRR27RkCUVFRcydO5fZs2dz9NFb3wgrLS0lP1/dzUVEYukGKt1i9+nuJcBKoHscdbOWBpJsfAoJuiGtMbMOwGXVtn8F7FjLuvnh8n3AP83sVWA6kA+MACaHGcdtqbUuMA0oB35jZncCPwSKgNdr2lH4jNdVYf31wHQze8ndZ9URg0idrvzg8HrXeXDSiITft89zmxKqn/P6zAbVe8X/kdD7Svw0YFx6/KH/88nbF8nb1+25uVyRl8f5f/4zp5x2Wq2DRJaWltK6rAzqOYjkK8kIUiIV1fXn83PHJVT/4GY/aVA9XX+knmJvoC4zswHA+zTsBmpDxmGqta6Z7U94AzUm8bAD8L/adlbDDdRr63j/JQSJj8r6BQSPeSwGSsPV+cDa8PX2MXWzdtw79XRofG4FWgErgHcIZpaIdRtwTDgwS+UUNZcDD4fZvGPdvRg4k+D5qdUEyYhT43nzbdV1903AUeHyKuA44Jma9hMOkPIocL27z3b3T4FLgb9Wex5LRESk0fg6JwcHvl62jLKyslrLlZaWUtBMP6NERKpJ5AZqpfuAX5rZ3hYoMLPDzKwwjvffVt3YG6jNzewoghuoNYq5gXoSwWMWvwuTKNvyOHCamQ0I2zzXAO+6+wJ3X06QfDjJzHLM7HRgp2r/Dj3CsSeyiq6WEXD3Ee4+IWb5VHf/ffh6Sbi9dTja+j3ubu5eHm6fFq5v7+6/Cdfd7e5dw0chngrXvezuQ8J1Xd39J5W9HNy9t7u/GvP+l7v7STHL26pb7O4DPZiX97jwz+9rOMZydy9y9+ti1t3l7v3D55tEREQanSvLyphSWsrUq6/mezvswBtvvLFVGXcPejoo6SAiUl2TvoEatrH+D3ia4NH0nYDjY4qcCVxI8MhFX2BqzLbXgA+BZWa2Ip7jzRR6vEJEREQkxm7AP9evZ2935n/yCSNGjKjaVtkjt7S0lMYyb0XBHlnbI1dEGiF3H1Ft+dSY10sIHq2OdU/M9mnAFtNsuvvdBNNFxq57ma0TFpXbeldbvrwedYuBgTVtq1aunGq9INz9LuCuOOpudTwx214imHGjpm2bgMPq2n8mSihFb2YdzOwVM/s0/Lt9LeU2h3OlzjKz5xJ5TxEREZFUewEo6dyZU08/vWrdnXfeyaeffoqZNaqkg4ikj9o/IvWXaE+Hi4FJ7n6dmV0cLl9UQ7kN7l7X8y8iIiKNSkMGjKtNMgaSqy7RgeVq09AB52pT00B0B9sxSdt/xfA6b1rVb3++mfc/u4fH7ryT3Nzgp9LEiRO58Pe/h0su4aQTT6T/HntQvOuu7Hj8cfXa9/EHTUlqrACcUON4ziKSGmr/NHFmdinBoxbVveXuh6Y7nkyQ6MOIRwIPh68fBn6U4P5EREREIrXsq/fYfccdGTNmDACbN2/m7HPPpfWRR9Jp3G95Zu6HnHfeeVS0aB5xpCISAbV/mjh3vyYcf6/6HyUcapFoT4ft3H1p+HoZsF0t5VqaWTHBaKHXufuzCb6viIiISNKVl29k8dLJPP7ka1XTZd7/wAOsAdr064uZUXjEEbQcOjTaQEUkKmr/iNRTnUkHM3uVLecPrTQ+dsHd3cxqG8mol7svNrMdgdfM7AN332o+VDM7CzgLwFq0+H7zLl3qPACAjm3WxVUOoFvuxrjLxmVOZk/E8Ak1Pob2ndat4t5XWbv4O850aFMSV7luuevj3qcR/1zpS8tbxl12xbp4ZucJbFq4aIW7d467goiINCqLl03hsMMOZcCAoFd0SUkJF48fT6ufnlCVhABo3llf9SLZSu2fbcj2tg+o/UP87Z/yVavYXFJaZxB1Jh3c/aDatpnZV2bW1d2XmllX4Ota9rE4/PszM3uDYMTQrT507n4vcC9AXs+e3m3ceXWFB8DPRk6OqxzAZZ3nxl02Hs26zk/q/tLtYBu5ze0Vg+J/TvbzI2udPWYr8T7TekXn2XHvM8fi/9Bfs2K3uMtOeP2AuMt+8ZsLvoi7sIhIBnH3LRrd2WjjxjUs+6qYG2/8sGrddTdcT06fPuTtsEOEkYlIOqn9U7tsb/uA2j8Qf/tn6Y23xlUu0TEdngNOMbPRwBygSzigShUza29mhWb2pJl9RjBP6ZoE31dERCQuZvaAmX1tZv+NOpZMUu7fstyX8FnzD5nT+m1mtXwr6pBSbvGyNxg79mx69OgBwJIlS/jTbX+m5cG1tj9EpOlR+0eknhId0+E64CngSmAGcBTwHzP7HzDK3c8Adgf+DuQBS4GHgDOBVxN870ZrW6Od12f08j7PbYq7bM7rM+ssU9Po4SKZ6g/9n69/HepfR7LCQ8DtwCMRx9GolZWVMW3aNCZNmsSHhe+wauMKBvTfi6OP/CllZRv5618eh8zuVbtN69YtZu26zxk//rsByX936aXkDxlC8w4dIoxMGpvIrj/nXZBQ9VcSj0ACav9UU9dMT/Vp/9RnVqa6ZlpS26fxSCjp4O4rzez3wOXuPgrAzJ4Adg4/cLj71PDu0uXuPs3McoFlZmbuXtszUCIiIknh7pPNrHfUcTQ2mzdvZtasWUyaNIlJkyYxdepUdt99d0aOHMkjTz/IsGHDaNUqeK513G8voEVJfsQRp467s2jpq1x11ZUUFgbPsc6ZM4dn//UvOl6QmmlJRSQzqf0jUn+J9nQA6A4sjFleBOxdWxl3Lzezb4COwIrYQrEDqeS0j2OQDxGRJqZgD/1WSYXY60+2P7lfUVHBKaecwosvvkiXLl0YOXIkv/rVr3jiiSdoX8u196033qJ1RTvqMV5VRlm56mNatqrgrLPOrFr36/POo9UBB5DTKv4BxUSyma4/W1D7R6QekpF0SJrqA6lEHI6IiDQRsdefwbWPRJ4VmjVrRpcuXSgoKODvf/87/fr1q7NOTvMc/tviPVa22o4WpQUUlLehLR3Is8xvkFf4ZhYtncQjj9xLbm7ws2jixInM+egjOpwf34BuIiINpfaPNAXJSDosBgaY2cdADjAfeLNaGQc+MLPKkf27ACuT8N4iIiJSTzfffDMDBw7kgAMO4I477uDYY4/dZvlp705l+fLlzJgxg3emvcObr0/m/Vlv0cyb0S63Ey3WtaLQ29GGDuRa8zQdRXIsW/Yeu+7ahzFjxgDBYydjf30uLUeNwnJyIo5ORBqpHsAJZjYCmEDQD2xx5UYzOxXYlWCqzPXAnUBb1P6RJioZSYf3gL2AkeHrFcAd1cq8D5i7DzCz44Gj9DyTiNTXJ7SPa6qj2lQMj38KpNrUZ2qkmsQ7XVKtTng9sfoioZNOOom+ffty1FFHUVxczDXXXFN1p78mnTt3ZsyYMVWNc3dnwYIFTJ8+nalvT+WtN99m2sczKMxrQ2FFO1qU5tOGDhTSNl2HVG/l5RtZvGwyf3tyUtV0oA888CAlJRXk9+sbcXTSmOj6g64/ITPLAc4hSCCMAf4BtACOrlZ0GjDP3X8Ztn9eU/tHmqpkJB2+TzBdzASCng6TgX5mNgQodvfngLeAfc1sPrCKYNqYrLWtUY01er6ISHqZ2ePACKCTmS0CLnP3+6ONqnEYOHAgxcXFnHDCCYwePZonnniCTp06bVFm2bJltGjRgg7VZnAwM/r06UOfPn047rjjACgvL2fu3LlMnz49SEJMmcrsRVMoatWGorIyhpSVUQTEP1N4ai1eNoUxY0YzcGDQICwpKeGSS8bTZ4ejWWlZOoCFiCSqiKBn923A8wTjNBS7+4dmdiVQHJabS3Ddyfr2T10zutSr/VOPWVo0I0vmSNZAku9XjtZqZicDe7v72Jgy3wIFwHLgy3BZRJo4M+tJMI3hdgSPYd3r7rdFG5VkG3c/IeoYGrOOHTvy0ksvMX78eAYPHswzzzzDoEGDqrYfMvIQPvrkI7p02o4hQ4aw/4E/oKioiIEDB1bNblEpNzeXPffckz333JMzzjgDgPXr1/P+++8zffp0Xp40iStnzGDFmjW0aVVMXmk+BeVtaUN78mhV1dsgHTZuXMOyr4q56aYPq9Zdf/0NFOT3pE1hD/WBzmK69kiCugML3f1F4MXKtg+Au/8Bqh6v+BFB22cWcL67L6x5dyLZL10DSf4beNzdy8zsF8DDwIHVC8WO3gqULDj/go9r2Fcnqo36emU9AqlP2TTb6rjSoq75ayfXY37byTWurfG4ro1zl/GWq7/59Sj7Qk0ra/v/6tWgcJqucmCcu880s0LgPTN7xd3nRh2YSFOSk5PDddddx+DBgxk1ahQ33ngjp556Khs3buTjTz9hv/LD2LCslE/+vZA5r9zD+hY3smr9Cnr36sO+w/Zlvx8Mo6ioiD322IOcauMg5OfnM2zYMIYNGwbnnw/AihUrKC4uZtq0aUx+7S3enz0F3wztczvRvKQVhRXtaUN7mluLlB3z4mVvMHbs2fTo0QOAJUuWcOufbqN/37PqqClZQNceSbW42j4QV/unxt+cWdD+aZxtH0i0/VPrcWV4+yehtk+yBpLsGbPcg5iBVCCYzzZmcQJwQ007ih29tTZmVuzugxsWauOl48os2Xpc6ebuS4Gl4et1ZjaP4A6CfviJROCYY45h9913Z3jR/kyb+g4nnXwi7Vt1JKc8l9a0pTVtoQwog82+mZL/rWHq/97nzaensLbZako3raPv9/oxfMR+DN13KEVFRfTq1WurHgydOnVi9OjRjB49Gq4Ixof48ssvg/EhpkzlrTfe5t2PZlDQopA2Fe1pXppPW9rTmnbkWOKDO65bt5i16z5n/PhLq9ZddNGlbNdlEK1aasq6bKdrjyQoaW2fsOw22z/Z+ptTx5VZEj2uZCQdZgC7mFkfgg/c8cBPYwuYWdfwCx7gCGBeEt5XRLKImfUGBgLvVltfdQegJflpj0ukqenbty97rh/GC49N5Mknn6Djpu1rLJdjObSlI23pCOuDdd/6JtbOXs3zH7zKPx/8N6u+XU6zXGO/vfpTNHIkRfvsw5AhQ+jYseMW+zIzevXqRa9evfjJT34CBLNIzJs3r2p8iKlTpjL7iyl0yO9E/qY2tNzYmja0p4A29To+d2fR0klcffUfKSwsBGDOnDk8++y/GLjn2DpqS7ap7doTbtP1R2qito9IPSWcdHD3cjMbC0wkGEjygdiBVMKBJH9jZkcQdGdbBZya6PuKSPYws9bA08B57r42dlvsHYA21kGjPkta1TU4Vr32lYqBhOsx4FZ9vAFUrIebc3PZp/xjhlPT047bUAGsDR6WXwxMf/ttZrzzDjcWFFC8YQOd2rWj/5B92X/kd+ND5Odv2ajLycmhX79+9OvXj9NPPx2ADRs2MGvWLKZPn86br01m+vTprFy1gk7/m0uL5l3Ib9WNNoU9yMtrW+v4ECtXfUzLVps588wzqtadc855dO86nOa5rWqsI9lpW9ce0PVHaqa2j0j9JWVMh8qBVKqt+0PM60uAS5LxXtTx+EUG03Fllmw9rrQzs+YEP/oec/dnoo5HRALNgAvLyxPahxH0O+4BHFVeDt98QwXw8ddfc8wLi5nz6r1szLuZleuX06tnL4YO25fh++9XNT5E9Sk8W7VqxdChQxk6dCjnnnsuAKtWraK4uJh33nmXN998m5kz/8Pm8s103X4IPbqN2KJ+hW9m0dJJPPLIvVX7njhxIv/94CP27PurhI5VMouuPZIItX2SQseVWRI6rnQNJJk0YdY56+i4Mku2Hle6WXAr8n6CeaxviToeEUm9ZsDuwKwllY8851FW1p05c0spnvU8b77yEjffsImFSzYwcK/vMWTvH7D33sFAlb17996qB0OHDh045JBDOOSQQ4Dg8Ylbb72V4inX8dfbP9+i7N0PryOn+Y6MGTMGCB7huGDc2dx3s/OjQx/Zouw1K1IzseeE1w9I+j5XrZuR9H1mM117JJNk629OHVdmSfS4Mi7pICJZZRhwMvCBmc0K110a3kEQkSYiL88YMrAlQwa2pLK/wTdrK3hv9tfMmPUYjz/yFON+W8rGjVBUNIAhRSPYe++hDBkyhM6dO2+xLzPjo4/mUDSgYov1a9dV8Mc/lfDSy3dWJS4efPAB2heu4cjRGjyyidG1R0QkjTIm6WBmo4HbCJ6dmuDu10UcUtKY2QJgHbAZKM/UEU/N7AHgcOBrd+8XrusAPAn0BhYAx7r76qhibIhajuty4EyC+ZdBP1YaxN3fJuiBLSKyhbZtmnHg8FYcOLxynIUCFi8tp3jWp8yYNY9bbriX4vfX0r59G4qKhlC09wEUFRUxaNAgZkyfwilHtNxifzfcUcKoUYczYMAAAEpKSrjsDxfzzAP5tY7/INlJ1x7JFNna/lHbp3FLRdsnI5IOZpYD3AEcDCwCZpjZc1k2n/IB7p7+uWqT6yHgdiC2j+rFwCR3v87MLg6XL4ogtkQ8xNbHBfAnd78p/eGISCp9QnsOtpFJ32/F8IFJ3+fnR+YlfZ8Axx80JSX7vaLz7ITqd++aS/euuRx5aLBcUVHAp599y3uzZjBj1nSe+pvz34/W4l7BgL47VNWrqHDueGAN552/G+Xl5eTm5nLTTdez/9AchgxoWcu7pc7GT+ez9rU36fKLn9daZtOixWz+Zi2t+u6exshEpLFoAu0ftX0ar4dIctunWaIRpUkRMN/dP3P3TcATwJERxyTVuPtkghF6Yx0JPBy+fhj4UVqDSoJajktERCLWrJmx284tOPknbfjz1W2Z9kI7VsztyUdv96Bly2ZblHvt6e14+/U76N9vJx5++GH+8uc/cdXFjXcaxE2Ll7BhrmbZE2nC1P5p5NT2iV9G9HQAugMLY5YXAXtHFEsqOPAfM3PgniwbgGS7mHmKlwHbRRlMko01s58BxcC4TOs6lYl22bOUF1+e2eD6OVWP7jZcooPLJTqInAaME9m2vDyjR7etf94M7J/HPx/M5cCjl3D++WNp2aKCd2dupHfP5vzxllU8/59SNmx0hg5uyd03BuNE3H/aNLp+ry1fzFzFpg3lHH3NACZPmM9Xn66j/6huHPSb4Ptg1r8X8c7fFrD5W6dH/3b88Pf9aJazZe/9T9/+mhevn8va8lnk7dinan3ZF1+y6ul/4d9+S7Pmzel44nHkduzAmhcn4pu+ZeNnC2h78IHkduywVbnm23VJ4b+kxNL1R9efCGRz+0dtn8zU4LZPpvR0yHb7ufsg4FDg12b2g6gDSgV3d4IvmWxwF7ATMABYCtwcbTgiIlKXia9vYEDfPFbM3Z6F73dj9AFBT4dfn9aWd1/uyZw3dmDDRuf5V9ZX1clpbvx34i6YAAAIPElEQVTqyf0oOrYXf/tNMT8c349z/vkD3v/XItav2cTXn63jvxOXcuYj+/LrfwynWQ7MfmHxFu/7bdlmnr38A066fQjbX3gem9eurdrWvEsXtj/3bLpd9FvaHjaKNc+/hOXm0m7MKAoG7UW3i35LwaABNZYTEclQavtknoTaPpnS02Ex0DNmuUe4Liu4++Lw76/N7J8E3akmRxtV0nxlZl3dfamZdQW+jjqgZHD3rypfm9l9wPMRhiMitTCzngTPJG5HcOG/191vizYqiUr/3Vtw4RUruPiqFRx2UAHD9wkGqXx9ygZuunM16zc4q9Zspu9uLWBQUOd7BwQ3qbbbpZAuOxVS2DkYA6J9j3y+WbaBL2auZsncb7j7hGAcjG/LNlPQYcuxNlZ8XkL77vl07FWAfWYUDPk+JVPfAaBi40ZWPfYE5cuXA4Zv3lxj7PGWE5GskbXtH7V9Mk+ibZ9MSTrMAHYxsz4EH7bjgZ9GG1JymFkB0Mzd14WvDwGujDisZHoOOAW4Lvz7X9GGkxyVXybh4o+B/0YZj4jUqpygC+BMMysE3jOzV7JoIC6ph113akHxf3ry4qT1/OH6lRw4PJ8Lz27H2EuWM/3lHvTs3pwrblrJxrIKmod1clsEnUKtmZHT4rsOotYMKjY7uDPgiB4cct73GhTTmhdepuUuO9HmjFMpX7mKZX+5K6FyIpI1srL9o7ZPZkq07ZMRj1e4ezkwFpgIzAOecvcPo40qabYD3jaz2cB04AV3fznimBrEzB4HpgG7mdkiM/s5wQfuYDP7FDgoXM4otRzXDWb2gZnNAQ4Azo80SBGpkbsvdfeZ4et1BNeQ7tFGJVFZsqyc/FbGSccUMu7s9sycU8bGsqDna6cOOZSUVvD086X12ueO+3Tiw1eWUrKyDID132xizZL1W5Tp1Kc1a5asZ9XCYN+l771fta1i40Zy2rYFoOTd756Zt7w8KsrK6iwnItkpi9s/avs0cqlo+2RKTwfCeUDjngs0U7j7Z8BeUceRDO5+Qi2bkj/3XBrVclz3pz0QEUmImfUGBgLvRhuJROWDeWVc9MeVNGsGzXONO67rTLu2OZxxYhv2POBLtu+Sy+AB9ZuGtMtOhRx0zm48/IvpeIWTk2scPr4f7bp9NzNG87wcjrysP389ewZryz8lb6c+lIcJhbYjR7Di0Sf4ZuKrW0yP2XKXnVn76mssuf4W2h58YK3lRCR7ZWP7R22fxi8VbR8LxrcQEWn8vr9Xnr/zco8G18+xxDt3RT16+NIbb6Xsy4VWd0mJZWatgTeBq939mRq2nwWcBdCS/O/vZ2OSHkPF8IFJ3+fnR9avgRyv4w+akpL9XtF5dkr2m4zPdnWJftZrk+h3QE30vZB6uv7oPBORhsuIxytEREQaysyaA08Dj9WUcABw93vdfbC7D25OahryIiIiIk2Rkg4iIpK1zMwIugTOc/dboo5HREREpKlR0kFERLLZMOBk4EAzmxX+Sf6zEyIiIiJSo4wZSFJERKS+3P1tQM8gi4iIiEREPR1EREREREREJCWUdBARERERERGRlFDSQURERERERERSQkkHEREREREREUkJJR1EREREREREJCWUdBARERERERGRlDB3jzoGEZG4mNly4IttFOkErEhTOFHF0MvdO6dw/01eHOdZrMZwzsVLsaZO1PHqeyHFdP0BdJ6JSAMp6SAiWcPMit19cFOPQdInk/6/FWvqZFq8knyN4RxoDDGIiNREj1eIiIiIiIiISEoo6SAiIiIiIiIiKaGkg4hkk3ujDoDGEYOkTyb9fyvW1Mm0eCX5GsM50BhiEBHZisZ0EBEREREREZGUUE8HEREREREREUkJJR1EJCuY2Wgz+9jM5pvZxRG8f08ze93M5prZh2Z2brpjkPSJ+nyrj0w8N80sx8zeN7Pno45lW8ysnZn9w8w+MrN5ZjY06pgkvaL+LsjEz7eIND16vEJEMp6Z5QCfAAcDi4AZwAnuPjeNMXQFurr7TDMrBN4DfpTOGCQ9GsP5Vh+ZeG6a2W+BwUAbdz886nhqY2YPA2+5+wQzawHku/uaqOOS9GgM3wWZ+PkWkaZHPR1EJBsUAfPd/TN33wQ8ARyZzgDcfam7zwxfrwPmAd3TGYOkTeTnW31k2rlpZj2Aw4AJUceyLWbWFvgBcD+Au29SwqHJify7INM+3yLSNCnpICLZoDuwMGZ5ERH+6DKz3sBA4N2oYpCUalTnW31kyLl5K/A7oCLqQOrQB1gOPBg+CjLBzAqiDkrSqlF9F2TI51tEmiAlHUREksjMWgNPA+e5+9qo4xGplAnnppkdDnzt7u9FHUsccoFBwF3uPhAoBRr1+B6SvTLh8y0iTZeSDiKSDRYDPWOWe4Tr0srMmhP86HvM3Z9J9/tL2jSK860+MujcHAYcYWYLCLqqH2hmj0YbUq0WAYvcvfKu8j8IkhDSdDSK74IM+nyLSBOlpIOIZIMZwC5m1icczO144Ll0BmBmRvBs9zx3vyWd7y1pF/n5Vh+ZdG66+yXu3sPdexP8u77m7idFHFaN3H0ZsNDMdgtXjQQ0eF/TEvl3QSZ9vkWk6VLSQUQynruXA2OBiQSDaD3l7h+mOYxhwMkEd2ZnhX/GpDkGSYNGcr7Vh87N1DkHeMzM5gADgGsijkfSqJF8F+jzLSKNnqbMFBEREREREZGUUE8HEREREREREUkJJR1EREREREREJCWUdBARERERERGRlFDSQURERERERERSQkkHEREREREREUkJJR1EREREREREJCWUdBARERERERGRlFDSQURERERERERS4v8BRKq8Jz7sXE8AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 1296x86.4 with 5 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "N = 4\n",
    "L = 2\n",
    "\n",
    "# create input signal\n",
    "x = np.sin(2 * np.pi * np.arange(16) / 44100 * 4400)\n",
    "\n",
    "# cut signal into frames of length N\n",
    "x = x.reshape(-1, N)\n",
    "\n",
    "# create lapped view of x\n",
    "x_lapped = lap(x, L)\n",
    "\n",
    "x_lapped[0, 6] = np.nan\n",
    "\n",
    "# reconstruct framed signal from lapped view\n",
    "x_out = unlap(x_lapped, L)\n",
    "\n",
    "# Plot data\n",
    "f, (a, b, c, d, e) = plt.subplots(1, 5, figsize=(18, 1.2))\n",
    "a.imshow(x.ravel()[None, :])\n",
    "a.set_title(\"flattened x\")\n",
    "b.imshow(x)\n",
    "b.set_title(\"x\")\n",
    "c.imshow(x_lapped)\n",
    "c.set_title(\"x_lapped\")\n",
    "d.imshow(x_out)\n",
    "d.set_title(\"x_out\")\n",
    "e.imshow(x_out.ravel()[None, :])\n",
    "e.set_title(\"flattened x_out\")\n",
    "\n",
    "a.add_patch(patches.Rectangle((3.5, -0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "b.add_patch(patches.Rectangle((-0.5, 0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "c.add_patch(patches.Rectangle((3.5, -0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "c.add_patch(patches.Rectangle((-0.5, 0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "d.add_patch(patches.Rectangle((-0.5, 0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "e.add_patch(patches.Rectangle((3.5, -0.5), 4, 1, linewidth=5, edgecolor='red', facecolor='none'))\n",
    "\n",
    "c.annotate('same data', xy=(2, 1), xytext=(5, 2), color='black', arrowprops={'facecolor': 'white', 'shrink': 0.05})\n",
    "c.annotate('', xy=(6, 0), xytext=(5, 1.8), arrowprops={'facecolor': 'white', 'shrink': 0.05})\n",
    "\n",
    "None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lapped Transforms\n",
    "\n",
    "In the previous Notebook, we derived how we can derive matrices that transform two frames at once. We will use those matrices here, so we will quickly recreate them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x201.6 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fl = 16\n",
    "\n",
    "D = dct4(fl)\n",
    "M = mdct(fl) * ss.cosine(2 * fl)\n",
    "tmp = D.T @ M\n",
    "F = make_twoframe(tmp)\n",
    "\n",
    "f, (a, b, c) = plt.subplots(1, 3, figsize=(18, 2.8))\n",
    "a.imshow(D)\n",
    "a.set_title(\"$\\mathbf{D}$\")\n",
    "b.imshow(M)\n",
    "b.set_title(\"$\\mathbf{D}$\")\n",
    "c.imshow(F)\n",
    "c.set_title(\"$\\mathbf{F}(z)$\")\n",
    "None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As a test we will try to run the filterbank to analyse and synthesize the input signal, and compare the outputs. In $z$-Domain Polyphase Matrices [2], we would express our operation as\n",
    "\n",
    "$$\n",
    "\\vec{x}_{\\mathrm{out}}(z) = \\mathbf{F}(z)^{-1} \\; \\mathbf{D}^{-1} \\; \\mathbf{D} \\; \\mathbf{F}(z) \\; \\vec{x}(z)\n",
    "$$\n",
    "\n",
    "Every polynomial matrix in this equation  ($\\mathbf{F}(z)$) will be processing more than one frame, so we need to temporarily switch to a lapped memory view using `lap()`. Afterwards, we can remove the overlap again using `unlap()`.\n",
    "\n",
    "Every real matrix in this equation ($\\mathbf{D}$) will only be processing one frame at a time, so no lapping/unlapping is required."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# create input signal\n",
    "x = np.sin(2 * np.pi * np.arange(2 * fl * 128) / 44100 * 440)\n",
    "\n",
    "# cut signal into frames\n",
    "x = x.reshape(-1, fl)\n",
    "\n",
    "# Time Domain Aliasing, requires lapped view\n",
    "tmp = lap(x)\n",
    "tmp = transform(tmp, F)\n",
    "tmp = unlap(tmp)\n",
    "\n",
    "# DCT-IV, requires single frame only\n",
    "X = transform(tmp, D)\n",
    "x_out = transform(X, D.T)\n",
    "\n",
    "# Time Domain Aliasing Cancellation, requires lapped view\n",
    "x_out = lap(x_out)\n",
    "x_out = transform(x_out, F.T)\n",
    "x_out = unlap(x_out)\n",
    "\n",
    "np.allclose(x, x_out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the last line we can see that our transform perfectly reconstructs the input signal.\n",
    "\n",
    "In essence, by overlapping our input signal and applying our folding matrix `F` on each overlapping frame, we have reproduced the transform that an infinitely long transform matrix would perform, without the massive amounts of unneccessary zeros in any of our matrices.\n",
    "\n",
    "## Time-Varying Transforms\n",
    "\n",
    "The most common variant of a time-varying MDCT is known as \"**window switching**\" [3]. To implement window switching in this framework, we can simply select the desired transform matrices `F` and `D` for each frame in `x`.\n",
    "\n",
    "For framelengths of `fl_l` and `fl_s`, thee matrices then turn out to be"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x432 with 8 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fl_l = 16  # \"long\" framelength\n",
    "fl_s = 4   # \"short\" framelength\n",
    "\n",
    "D_l = dct4(fl_l)\n",
    "M_l = mdct(fl_l) * ss.cosine(2 * fl_l)\n",
    "tmp = D_l.T @ M_l\n",
    "F_l = make_twoframe(tmp, trim=True)\n",
    "\n",
    "D_s = dct4(fl_s)\n",
    "M_s = mdct(fl_s) * ss.cosine(2 * fl_s)\n",
    "tmp = D_s.T @ M_s\n",
    "F_s = make_twoframe(tmp, trim=True)\n",
    "\n",
    "D_long = D_l\n",
    "D_short = scipy.linalg.block_diag(*[D_s] * (fl_l // fl_s))\n",
    "\n",
    "F_long = scipy.linalg.block_diag(np.eye(fl_l // 2), F_l, np.eye(fl_l // 2))\n",
    "F_start = scipy.linalg.block_diag(np.eye(fl_l - fl_s // 2), F_s, np.eye(fl_l - fl_s // 2))\n",
    "F_short = scipy.linalg.block_diag(np.eye(fl_s // 2), *[F_s] * (fl_l // fl_s) , np.eye(fl_l - fl_s // 2))\n",
    "F_stop = F_short\n",
    "\n",
    "fig, ((a, b, c, d), (e, f, g, h)) = plt.subplots(2, 4, figsize=(18, 6))\n",
    "a.imshow(D_long)\n",
    "a.set_title(\"$\\mathbf{D}_\\mathrm{long}$\")\n",
    "b.set_visible(False)\n",
    "c.imshow(D_short)\n",
    "c.set_title(\"$\\mathbf{D}_\\mathrm{short}$\")\n",
    "d.set_visible(False)\n",
    "\n",
    "e.imshow(F_long)\n",
    "e.set_title(\"$\\mathbf{F}_\\mathrm{long}$\")\n",
    "f.imshow(F_start)\n",
    "f.set_title(\"$\\mathbf{F}_\\mathrm{start}$\")\n",
    "g.imshow(F_short)\n",
    "g.set_title(\"$\\mathbf{F}_\\mathrm{short}$\")\n",
    "h.imshow(F_stop)\n",
    "h.set_title(\"$\\mathbf{F}_\\mathrm{stop}$\")\n",
    "None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Naturally, selecting the correct folding matrix in the correct situation is crucial, so the following rules need to be observed:\n",
    "\n",
    "| Current $\\mathbf{D}$ | Next $\\mathbf{D}$ | $\\rightarrow$ Current $\\mathbf{F}$ |\n",
    "|----------------------|-------------------|------------------------------------|\n",
    "| long                 | long              | long                               |\n",
    "| long                 | short             | start                              |\n",
    "| short                | short             | short                              |\n",
    "| short                | long              | stop                               |\n",
    "\n",
    "\n",
    "# References\n",
    "\n",
    " 1. Werner, Nils and Edler, Bernd, \"[Experimenting with Lapped Transforms in Numerical Computation Libraries using Polyphase Matrices and Strided Memory Views](http://www.aes.org/e-lib/browse.cfm?elib=20381)\". Audio Engineering Society Convention 146, 2019\n",
    " 1. Schuller, G. D. T. and Smith, M. J. T., \"[New framework for modulated perfect reconstruction filter banks](https://ieeexplore.ieee.org/document/533715)\". IEEE Transactions on Signal Processing, 44(8), pp. 1941–1954, 1996, ISSN 1053-587X, doi:10.1109/78.533715.\n",
    " 1. Edler, Bernd, \"[Codierung von Audiosignalen mit überlappender Transformation und adaptiven Fensterfunktionen](https://www.degruyter.com/view/j/freq.1989.43.9/freq.1989.43.9.252/freq.1989.43.9.252.xml)\", Frequenz, Bd. 43, S. 252–256, Sep. 1989."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "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.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}