{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "wav_read_write_frame.py\n",
    "Author: M A Rohit\n",
    "This program is tested on Ubuntu 18.04 with Python 3.6.9 and Numpy 1.19.2.\n",
    "It requires the installation of the additional package found here: https://github.com/vokimon/python-wavefile\n",
    "This package is a wrapper for a system package (non-python) called 'libsndfile1', which needs to be installed separately using 'sudo apt install ..'.\n",
    "\n",
    "Program flow:\n",
    "1. open a wav file for reading; open another wav file for writing\n",
    "2. Next, read audio sequentially in smaller chunks from the wav file, one frame* at a time, in a loop:\n",
    "     3. load hop_len number of samples of audio\n",
    "     4. do some processing\n",
    "     5. write out  hop_len samples to the output wav file\n",
    "     6. write the feature values to a text file\n",
    "\n",
    "* A \"frame\" is a chunk of 'hop_len' number of samples, loaded in each iteration. This is distinct from a \"window\", which is the set of frames stored in the buffer and available for processing in each iteration. The loaded hop_len #samples are appended to the window (on the right side), and hop_len #samples at the start of the window are discarded (akin to the hop between overlapping windows in STFT)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Import modules\n",
    "import sys\n",
    "from wavefile import WaveReader, WaveWriter\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Parameters\n",
    "hop_size=10e-3 #10 ms\n",
    "win_size=50e-3 #50 ms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Specify input wav file, output wav file (to store modified input audio), \n",
    "#and output features file (to save extracted features)\n",
    "\n",
    "in_file = '\\path\\to\\input\\wav\\file'\n",
    "out_file = '\\path\\to\\output\\wav\\file'\n",
    "feat_file = '\\path\\to\\output\\features\\file'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#an empty list to store computed feature values\n",
    "feat_vals=[]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#main loop\n",
    "with WaveReader(in_file) as r:\n",
    "\t#get window and hop sizes in samples\n",
    "\twin_len=int(r.samplerate*win_size)\n",
    "\thop_len=int(r.samplerate*hop_size)\n",
    "\t\n",
    "\t#initialise data array of size win_len; successive frames of the audio will be pushed \n",
    "    #into this array from the right\n",
    "\tdata=np.zeros([r.channels,win_len])\n",
    "    \n",
    "\twith WaveWriter(out_file, channels=r.channels, samplerate=r.samplerate) as w :\n",
    "\t\tfor frame in r.read_iter(size=hop_len):\n",
    "\t\t\t#first discard the starting hop_len samples\n",
    "\t\t\t#(using frame.size instead of hop_len to handle the last iteration \n",
    "            #where hop_len #samples may not be read. A fix: zero pad loaded frame)\n",
    "\t\t\tdata[:r.channels,:(win_len-frame.size)]=data[:r.channels,frame.size:]\n",
    "\t\t\tdata[:r.channels,win_len-frame.size:]=frame\n",
    "\t\t\t\n",
    "\t\t\t#Do some processing: perform operations on 'data' or 'frame'\n",
    "\t\t\t#(use 'frame' if modifying audio (e.g., filtering) and writing to output wav, else, \n",
    "            #use 'data' to compute short-time features (e.g., short-time energy))\n",
    "            \n",
    "\t\t\t#example operations:\n",
    "\t\t\tframe*=0.5\t\t\t\t\t\t\t#reducing amplitude of audio, performed on 'frame'\n",
    "\t\t\tfeat_vals.append((data**2).sum())\t#computing short-time energy, performed on 'data'\n",
    "\t\t\t\n",
    "\t\t\t#Write the audio frames to the output file\n",
    "\t\t\tw.write(frame)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Writing the feature values to a text file\n",
    "times = np.arange(0, hop_size*len(feat_vals), hop_size)\n",
    "feats = np.array((times, feat_vals)).T\n",
    "np.savetxt(feat_file, feats, fmt=\"%.3f\", delimiter='\\t', header='Time\\tFeature', comments='')"
   ]
  }
 ],
 "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.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
