{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\nBat call example\n^^^^^^^^^^^^^^^^\nThe <INSERTNEWNAME> package has many example recordings of bat calls thanks to\nthe generous contributions of bioacousticians around the world:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\nimport numpy as np\nimport itsfm \nfrom itsfm.run_example_analysis import contributors\nprint(contributors)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from itsfm.data import example_calls, all_wav_files"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Separating the constant frequency (CF) and frequency-modulated parts of a call\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nHere, let's take an example *R. mehelyi/euryale(?)* call recording. These\nbats emit what are called 'CF-FM'  calls. This is what it looks like. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "bat_rec = list(map( lambda X: '2018-08-17_34_134' in X, all_wav_files))\nindex = bat_rec.index(True)\naudio, fs = example_calls[index] # load the relevant example audio\n\nw,s = itsfm.visualise_sound(audio,fs, fft_size=128)\n# set the ylim of the spectrogram narrow to check out the call in more detail\ns.set_ylim(60000, 125000)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now, let's segment and get some basic measurements from this call. Ignore the \nactual parameter settings for now. We'll ease into it later !\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "non_default_parameters = {\n                          'segment_method':'pwvd',\n                          'signal_level':-26, # dBrms re 1\n                          'fmrate_threshold':2.0, # kHz/ms\n                          'max_acc':2.0, # kHz/ms^2\n                          'window_size':int(fs*0.0015) # number of samples\n                          }\noutputs = itsfm.segment_and_measure_call(audio, fs, \n                                        **non_default_parameters)\n\n# load the results into a convenience class \n# itsFMinspector parses the output and creates diagnostic plots\n# and access to the underlying diagnostic data itself\n\noutput_inspect = itsfm.itsFMInspector(outputs, audio, fs)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's check that the threshold we chose actually matches the region \nof audio we're interested in \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "output_inspect.visualise_geq_signallevel()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's take a look at how long the different parts of the call are. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "output_inspect.measurements"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Verifying the CF-FM segmentations\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nHere, let's see where the calls are in time and how they match the spectrogram output\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "output_inspect.visualise_cffm_segmentation()\nplt.tight_layout()\nplt.savefig('pwvd_cffm_segmentation.png')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Even without understanding what's happening here, you can see the \n'sloped' regions are within the red boxes, and the 'relatively even \nregion is in the black box. These are the FM and CF parts of this call.\n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The underlying frequency profile of a sound\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nThe CF and FM parts of a call in the 'pwvd' method is based on actually\ntracking the instantaneous frequency of the call with high temporal \nresolution. With this profile, the rate of frequency change, or modulation\ncan be calculated for each region. Using a threshold rate of the\nfrequency modulation, call regions above and below it can be easily identified!\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "s,w = output_inspect.visualise_frequency_profiles()\ns.legend_.remove()\n\nhandles, labels = s.get_legend_handles_labels()\nlabels_new = ['Raw frequency profile (FP)','Error corrected FP','Downsampled FP']\nl = s.legend(handles, labels_new, loc=8, fontsize=11,\n               borderaxespad=0., frameon=False, labelcolor='w')\ns.set_ylabel('Frequency, Hz', labelpad=-1.5)\nplt.savefig('pwvd_freqprofiles.png')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "You can see from the plot above that the frequency profile of the sound\nshows a relatively constant frequency region of the call in middle and \nwith frequency modulated regions in the middle.\n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The underlying frequency modulation rate\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fmrate_plot, spec, waveform = output_inspect.visualise_fmrate()\nfmrate_plot.hlines(2,0,audio.size/fs, linestyle='dotted',label='2 kHz threshold')\nfmrate_plot.legend(frameon=False)\nplt.savefig('pwvd_fmrate_diagnostic.png')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Performing measurements on the CF and FM parts of a call\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nWe were just able to get some measurements on the Cf and FM \nparts of the call. What if we want *more* information, eg. the \nrms, and peak frequency of each CF and FM call part? This is \nwhere <insertname> has a bunch of inbuilt and customisable \nmeasurement functions. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "inbuilt_measures = [itsfm.measure_peak_frequency,\n                       itsfm.measure_rms]\n\nnon_default_parameters['measurements'] = inbuilt_measures"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The :code:`output` is a tuple with 3 objects in it related to the segmentation\nindividual call parts and the measurements made on them.\nWe're happy with the actual segmentation, and so won' be making anymore diagnostic\nplots, and won' need to call :code:`itsFMInspector` anymore. \nWe can unpack the outputs into its components and just view the measurements. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "seg_out, call_parts, results_inbuilt = itsfm.segment_and_measure_call(audio, fs, \n                                             **non_default_parameters\n                                                 )\nresults_inbuilt"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The results are output as a pandas DataFrame, which means they can be easily\nsaved as a csv file if you were to run it in your system. Each row corresponds\nto one identified CF or FM region in an audio recording. \n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Defining custom measurements\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nIf the inbuilt measurement functions are not enough - then you may \nwant to write your own. See the documentation for what a measurement\nfunction must look like by typing :code:`help(itsfm.measurement_function)`. \nThe 'peak_to_peak' function below calculates the difference\nbetween the highest negative and highest positive value. This effectively \nthe maximum range of values that the signal takes. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "def peak_to_peak(whole_audio, fs, segment, **kwargs):\n    '''\n    Calculates the range between the minimum and the maximum of the audio \n    samples. \n    '''\n    relevant_audio = whole_audio[segment]\n    peak2peak = np.max(relevant_audio) - np.min(relevant_audio)\n    return {'peak2peak':peak2peak}\n\ncustom_measure_fn = [peak_to_peak]\n\n# add the custom_measure list to the :code:`non_default_parameters` dictionary \n# \nnon_default_parameters['measurements'] = custom_measure_fn \n\n\n\nseg_out, call_parts, results_custom = itsfm.segment_and_measure_call(audio, fs, \n                                                                   **non_default_parameters\n                                                 )\nresults_custom"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Of course, needless to say, you can also mix and match inbuilt with \ncustom defined measurement functions. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "mixed_measures = [peak_to_peak, itsfm.measure_rms]\nnon_default_parameters['measurements'] = mixed_measures\n\nseg_out, call_parts, results_mixed = itsfm.segment_and_measure_call(audio, fs, \n                                             **non_default_parameters\n                                                 )\nresults_mixed"
      ]
    }
  ],
  "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.9"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}