{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Date: Jun 2019<br>Programmer: Christian Dittmar, Yiğitcan Özer\n",
    "\n",
    "#### If you use the 'NMF toolbox' please refer to \n",
    "[1] Patricio López-Serrano, Christian Dittmar, Yiğitcan Özer, and Meinard Müller<br>NMF Toolbox: Music Processing Applications of Nonnegative Matrix Factorization<br>In Proceedings of the International Conference on Digital Audio Effects (DAFx), 2019.\n",
    "\n",
    "#### License:\n",
    "This file is part of 'NMF toolbox'. 'NMF toolbox' is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 'NMF toolbox' is distributed in the hope that it will be useful, but ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.\n",
    "\n",
    "#### You should have received a copy of the GNU General Public License along<br>with 'NMF toolbox'. If not, see http://www.gnu.org/licenses/."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Initialization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import scipy.io.wavfile as wav\n",
    "import matplotlib.pyplot as plt\n",
    "import IPython.display as ipd\n",
    "\n",
    "from NMFtoolbox.utils import EPS, MAX_WAV_VALUE, make_monaural, pcmInt16ToFloat32Numpy\n",
    "from NMFtoolbox.forwardSTFT import forwardSTFT\n",
    "from NMFtoolbox.NMFdiag import NMFdiag\n",
    "from NMFtoolbox.visualizeComponentsNMF import visualizeComponentsNMF\n",
    "from NMFtoolbox.LSEE_MSTFTM_GriffinLim import LSEE_MSTFTM_GriffinLim\n",
    "\n",
    "inpPath = '../data/'\n",
    "outPath = 'output/'\n",
    "\n",
    "# create the output directory if it doesn't exist\n",
    "if not os.path.isdir(outPath):\n",
    "    os.makedirs(outPath)\n",
    "\n",
    "filenameSource = 'Bees_Buzzing.wav'\n",
    "filenameTarget = 'Beatles_LetItBe.wav'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Load the source and target signal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# read signals\n",
    "fs, xs = wav.read(os.path.join(inpPath, filenameSource))\n",
    "fs, xt = wav.read(os.path.join(inpPath, filenameTarget))\n",
    "\n",
    "# make monaural if necessary\n",
    "make_monaural(xs)\n",
    "make_monaural(xt)\n",
    "\n",
    "# convert wavs from int16 to float32\n",
    "xs = pcmInt16ToFloat32Numpy(xs)\n",
    "xt = pcmInt16ToFloat32Numpy(xt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Compute STFT of both signals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "paramSTFT = dict()\n",
    "paramSTFT['blockSize'] = 2048\n",
    "paramSTFT['hopSize'] = 1024\n",
    "paramSTFT['winFunc'] = np.hanning(paramSTFT['blockSize'])\n",
    "paramSTFT['reconstMirror'] = True\n",
    "paramSTFT['appendFrame'] = True\n",
    "paramSTFT['numSamples'] = len(xt)\n",
    "\n",
    "# STFT computation\n",
    "Xs, As, Ps = forwardSTFT(xs, paramSTFT)\n",
    "Xt, At, Pt = forwardSTFT(xt, paramSTFT)\n",
    "\n",
    "# get dimensions and time and freq resolutions\n",
    "numBins, numTargetFrames = Xt.shape\n",
    "numBins, numSourceFrames = Xs.shape\n",
    "deltaT = paramSTFT['hopSize'] / fs\n",
    "deltaF = fs / paramSTFT['blockSize']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Apply continuity NMF variants to mosaicing pair"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# initialize activations randomly\n",
    "H0 = np.random.rand(numSourceFrames, numTargetFrames)\n",
    "\n",
    "# init templates by source frames\n",
    "W0 = As * 1./ (EPS + np.sum(As, axis=0))\n",
    "Xs = Xs * 1./ (EPS + np.sum(As, axis=0))\n",
    "\n",
    "# parameters taken from Jonathan Driedger's toolbox\n",
    "paramNMFdiag = dict()\n",
    "paramNMFdiag['fixW'] = True\n",
    "paramNMFdiag['numOfIter'] = 20\n",
    "paramNMFdiag['continuity'] = dict()\n",
    "paramNMFdiag['continuity']['polyphony'] = 10\n",
    "paramNMFdiag['continuity']['length'] = 7\n",
    "paramNMFdiag['continuity']['grid'] = 1\n",
    "paramNMFdiag['continuity']['sparsen'] = [1, 7]\n",
    "\n",
    "# call the reference implementation as provided by Jonathan Driedger\n",
    "nmfdiagW, nmfdiagH = NMFdiag(At, W0, H0, paramNMFdiag)\n",
    "\n",
    "# create mosaic, replacing the magnitude frames by complex valued frames\n",
    "contY = np.matmul(Xs, nmfdiagH)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 374752 characters]\n",
      "text/plain": [
       "<Figure size 1440x1440 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# visualize\n",
    "paramVis = {}\n",
    "paramVis['deltaF'] = deltaF\n",
    "paramVis['deltaT'] = deltaT\n",
    "paramVis['fontSize'] = 24\n",
    "\n",
    "fh1, _ = visualizeComponentsNMF(At, nmfdiagW, nmfdiagH, None, paramVis)\n",
    "fh1.savefig(os.path.join(outPath, 'LetItBee_NMFdiag.png'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# resynthesize using Griffin-Lim, 50 iterations by default\n",
    "Xout, Pout, res = LSEE_MSTFTM_GriffinLim(contY, paramSTFT)\n",
    "\n",
    "# save result\n",
    "wav.write(filename=os.path.join(outPath, 'LetItBee_NMFdiag_with_target_' + filenameTarget),\n",
    "          rate=fs,\n",
    "          data=res*MAX_WAV_VALUE)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio controls=\"controls\" >\n",
       "                    <source src=\"data:audio/wav;base64,[encoded data, 1176060 characters]\" type=\"audio/wav\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# play\n",
    "ipd.Audio(res.T, rate=fs)"
   ]
  }
 ],
 "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.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
