[
  {
    "path": "COPYING",
    "content": "Copyright (c) 1998, Malcolm Slaney <malcolm@interval.com>\nCopyright (c) 2009, Dan Ellis <dpwe@ee.columbia.edu>\nCopyright (c) 2014, Jason Heeris <jason.heeris@gmail.com>\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * Neither the name of the copyright holder nor the names of its contributors\n      may be used to endorse or promote products derived from this software\n      without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "Gammatone Filterbank Toolkit\n============================\n\n*Utilities for analysing sound using perceptual models of human hearing.*\n\nJason Heeris, 2013\n\nSummary\n-------\n\nThis is a port of Malcolm Slaney's and Dan Ellis' gammatone filterbank MATLAB\ncode, detailed below, to Python 2 and 3 using Numpy and Scipy. It analyses signals by\nrunning them through banks of gammatone filters, similar to Fourier-based\nspectrogram analysis.\n\n![Gammatone-based spectrogram of Für Elise](doc/FurElise.png)\n\nInstallation\n------------\n\nYou can install directly from this git repository using:\n\n```text\npip install git+https://github.com/detly/gammatone.git\n```\n\n...or you can clone the git repository however you prefer, and do:\n\n```text\npip install .\n```\n\n...or:\n\n```\npython setup.py install\n```\n\n...from the cloned tree.\n\n### Dependencies\n\n - numpy\n - scipy\n - nose\n - mock\n - matplotlib\n\nUsing the Code\n--------------\n\nSee the [API documentation](http://detly.github.io/gammatone/). For a\ndemonstration, find a `.wav` file (for example,\n[Für Elise](http://heeris.id.au/samples/FurElise.wav)) and run:\n\n```text\npython -m gammatone FurElise.wav -d 10\n```\n\n...to see a gammatone-gram of the first ten seconds of the track. If you've\ninstalled via `pip` or `setup.py install`, you should also be able to just run:\n\n```text\ngammatone FurElise.wav -d 10\n```\n\nBasis\n-----\n\nThis project is based on research into how humans perceive audio, originally\npublished by Malcolm Slaney:\n\n[Malcolm Slaney (1998) \"Auditory Toolbox Version 2\", Technical Report #1998-010,\nInterval Research Corporation, 1998.](\nhttp://cobweb.ecn.purdue.edu/~malcolm/interval/1998-010/\n)\n\nSlaney's report describes a way of modelling how the human ear perceives,\nemphasises and separates different frequencies of sound. A series of gammatone\nfilters are constructed whose width increases with increasing centre frequency,\nand this bank of filters is applied to a time-domain signal. The result of this\nis a spectrum that should represent the human experience of sound better than,\nsay, a Fourier-domain spectrum would.\n\nA gammatone filter has an impulse response that is a sine wave multiplied by a\ngamma distribution function. It is a common approach to modelling the auditory\nsystem.\n\nThe gammatone filterbank approach can be considered analogous (but not\nequivalent) to a discrete Fourier transform where the frequency axis is\nlogarithmic. For example, a series of notes spaced an octave apart would appear\nto be roughly linearly spaced; or a sound that was distributed across the same\nlinear frequency range would appear to have more spread at lower frequencies.\n\nThe real goal of this toolkit is to allow easy computation of the gammatone\nequivalent of a spectrogram — a time-varying spectrum of energy over audible\nfrequencies based on a gammatone filterbank.\n\nSlaney demonstrated his research with an initial implementation in MATLAB. This\nimplementation was later extended by Dan Ellis, who found a way to approximate a\n\"gammatone-gram\" by using the fast Fourier transform. Ellis' code calculates a\nmatrix of weights that can be applied to the output of a FFT so that a\nFourier-based spectrogram can easily be transformed into such an approximation.\n\nEllis' code and documentation is here: [Gammatone-like spectrograms](\nhttp://labrosa.ee.columbia.edu/matlab/gammatonegram/\n)\n\nInterest\n--------\n\nI became interested in this because of my background in science communication\nand my general interest in the teaching of signal processing. I find that the\nspectrogram approach to visualising signals is adequate for illustrating\nabstract systems or the mathematical properties of transforms, but bears little\ncorrespondence to a person's own experience of sound. If someone wants to see\nwhat their favourite piece of music \"looks like,\" a normal Fourier transform\nbased spectrogram is actually quite a poor way to visualise it. Features of the\naudio seem to be oddly spaced or unnaturally emphasised or de-emphasised\ndepending on where they are in the frequency domain.\n\nThe gammatone filterbank approach seems to be closer to what someone might\nintuitively expect a visualisation of sound to look like, and can help develop\nan intuition about alternative representations of signals.\n\nVerifying the port\n------------------\n\nSince this is a port of existing MATLAB code, I've written tests to verify the\nPython implementation against the original code. These tests aren't unit tests,\nbut they do generally test single functions. Running the tests has the same\nworkflow:\n\n  1. Run the scripts in the `test_generation` directory. This will create a\n     `.mat` file containing test data in `tests/data`.\n\n  2. Run `nosetest3` in the top level directory. This will find and run all the\n     tests in the `tests` directory.\n\nAlthough I'm usually loathe to check in generated files to version control, I'm\nwilling to make an exception for the `.mat` files containing the test data. My\nreasoning is that they represent the decoupling of my code from the MATLAB code,\nand if the two projects were separated, they would be considered a part of the\nPython code, not the original MATLAB code.\n\n"
  },
  {
    "path": "auditory_toolkit/COPYING",
    "content": "Copyright (c) 1998, Malcolm Slaney <malcolm@interval.com>\nCopyright (c) 2009, Dan Ellis <dpwe@ee.columbia.edu>\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * Neither the name of the copyright holder nor the names of its contributors\n      may be used to endorse or promote products derived from this software\n      without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "auditory_toolkit/ERBFilterBank.m",
    "content": "function output = ERBFilterBank(x, fcoefs)\n% function output = ERBFilterBank(x, fcoefs)\n% Process an input waveform with a gammatone filter bank. This function \n% takes a single sound vector, and returns an array of filter outputs, one \n% channel per row.\n%\n% The fcoefs parameter, which completely specifies the Gammatone filterbank,\n% should be designed with the MakeERBFilters function.  If it is omitted,\n% the filter coefficients are computed for you assuming a 22050Hz sampling\n% rate and 64 filters regularly spaced on an ERB scale from fs/2 down to 100Hz.\n%\n\n% Malcolm Slaney @ Interval, June 11, 1998.\n% (c) 1998 Interval Research Corporation  \n% Thanks to Alain de Cheveigne' for his suggestions and improvements.\n\nif nargin < 1\n\terror('Syntax: output_array = ERBFilterBank(input_vector[, fcoefs]);');\nend\n\nif nargin < 2\n\tfcoefs = MakeERBFilters(22050,64,100);\nend\n\nif size(fcoefs,2) ~= 10\n\terror('fcoefs parameter passed to ERBFilterBank is the wrong size.');\nend\n\nif size(x,2) < size(x,1)\n\tx = x';\nend\n\nA0  = fcoefs(:,1);\nA11 = fcoefs(:,2);\nA12 = fcoefs(:,3);\nA13 = fcoefs(:,4);\nA14 = fcoefs(:,5);\nA2  = fcoefs(:,6);\nB0  = fcoefs(:,7);\nB1  = fcoefs(:,8);\nB2  = fcoefs(:,9);\ngain= fcoefs(:,10);\t\n\noutput = zeros(size(gain,1), length(x));\nfor chan = 1: size(gain,1)\n\ty1=filter([A0(chan)/gain(chan) A11(chan)/gain(chan) ...\n\t\t   A2(chan)/gain(chan)], ...\n\t\t\t\t[B0(chan) B1(chan) B2(chan)], x);\n\ty2=filter([A0(chan) A12(chan) A2(chan)], ...\n\t\t\t\t[B0(chan) B1(chan) B2(chan)], y1);\n\ty3=filter([A0(chan) A13(chan) A2(chan)], ...\n\t\t\t\t[B0(chan) B1(chan) B2(chan)], y2);\n\ty4=filter([A0(chan) A14(chan) A2(chan)], ...\n\t\t\t\t[B0(chan) B1(chan) B2(chan)], y3);\n\toutput(chan, :) = y4;\nend\n\nif 0\n\tsemilogx((0:(length(x)-1))*(fs/length(x)),20*log10(abs(fft(output))));\nend\n"
  },
  {
    "path": "auditory_toolkit/ERBSpace.m",
    "content": "function cfArray = ERBSpace(lowFreq, highFreq, N)\n% function cfArray = ERBSpace(lowFreq, highFreq, N)\n% This function computes an array of N frequencies uniformly spaced between\n% highFreq and lowFreq on an ERB scale.  N is set to 100 if not specified.\n%\n% See also linspace, logspace, MakeERBCoeffs, MakeERBFilters.\n%\n% For a definition of ERB, see Moore, B. C. J., and Glasberg, B. R. (1983).\n% \"Suggested formulae for calculating auditory-filter bandwidths and\n% excitation patterns,\" J. Acoust. Soc. Am. 74, 750-753.\n\nif nargin < 1\n\tlowFreq = 100;\nend\n\nif nargin < 2\n\thighFreq = 44100/4;\nend\n\nif nargin < 3\n\tN = 100;\nend\n\n% Change the following three parameters if you wish to use a different\n% ERB scale.  Must change in MakeERBCoeffs too.\nEarQ = 9.26449;\t\t\t\t%  Glasberg and Moore Parameters\nminBW = 24.7;\norder = 1;\n\n% All of the followFreqing expressions are derived in Apple TR #35, \"An\n% Efficient Implementation of the Patterson-Holdsworth Cochlear\n% Filter Bank.\"  See pages 33-34.\ncfArray = -(EarQ*minBW) + exp((1:N)'*(-log(highFreq + EarQ*minBW) + ...\n\t\tlog(lowFreq + EarQ*minBW))/N) * (highFreq + EarQ*minBW);\n\n"
  },
  {
    "path": "auditory_toolkit/MakeERBFilters.m",
    "content": "function [fcoefs,cf]=MakeERBFilters(fs,numChannels,lowFreq)\n% function [fcoefs,cf]=MakeERBFilters(fs,numChannels,lowFreq)\n% This function computes the filter coefficients for a bank of \n% Gammatone filters.  These filters were defined by Patterson and \n% Holdworth for simulating the cochlea.  \n% \n% The result is returned as an array of filter coefficients.  Each row \n% of the filter arrays contains the coefficients for four second order \n% filters.  The transfer function for these four filters share the same\n% denominator (poles) but have different numerators (zeros).  All of these\n% coefficients are assembled into one vector that the ERBFilterBank \n% can take apart to implement the filter.\n%\n% The filter bank contains \"numChannels\" channels that extend from\n% half the sampling rate (fs) to \"lowFreq\".  Alternatively, if the numChannels\n% input argument is a vector, then the values of this vector are taken to\n% be the center frequency of each desired filter.  (The lowFreq argument is\n% ignored in this case.)\n\n% Note this implementation fixes a problem in the original code by\n% computing four separate second order filters.  This avoids a big\n% problem with round off errors in cases of very small cfs (100Hz) and\n% large sample rates (44kHz).  The problem is caused by roundoff error\n% when a number of poles are combined, all very close to the unit\n% circle.  Small errors in the eigth order coefficient, are multiplied\n% when the eigth root is taken to give the pole location.  These small\n% errors lead to poles outside the unit circle and instability.  Thanks\n% to Julius Smith for leading me to the proper explanation.\n\n% Execute the following code to evaluate the frequency\n% response of a 10 channel filterbank.\n%\tfcoefs = MakeERBFilters(16000,10,100);\n%\ty = ERBFilterBank([1 zeros(1,511)], fcoefs);\n%\tresp = 20*log10(abs(fft(y')));\n%\tfreqScale = (0:511)/512*16000;\n%\tsemilogx(freqScale(1:255),resp(1:255,:));\n%\taxis([100 16000 -60 0])\n%\txlabel('Frequency (Hz)'); ylabel('Filter Response (dB)');\n\n% Rewritten by Malcolm Slaney@Interval.  June 11, 1998.\n% (c) 1998 Interval Research Corporation  \n\nT = 1/fs;\nif length(numChannels) == 1\n\tcf = ERBSpace(lowFreq, fs/2, numChannels);\nelse\n\tcf = numChannels(1:end);\n\tif size(cf,2) > size(cf,1)\n\t\tcf = cf';\n\tend\nend\n\n% Change the followFreqing three parameters if you wish to use a different\n% ERB scale.  Must change in ERBSpace too.\nEarQ = 9.26449;\t\t\t\t%  Glasberg and Moore Parameters\nminBW = 24.7;\norder = 1;\n\nERB = ((cf/EarQ).^order + minBW^order).^(1/order);\nB=1.019*2*pi*ERB;\n\nA0 = T;\nA2 = 0;\nB0 = 1;\nB1 = -2*cos(2*cf*pi*T)./exp(B*T);\nB2 = exp(-2*B*T);\n\nA11 = -(2*T*cos(2*cf*pi*T)./exp(B*T) + 2*sqrt(3+2^1.5)*T*sin(2*cf*pi*T)./ ...\n\t\texp(B*T))/2;\nA12 = -(2*T*cos(2*cf*pi*T)./exp(B*T) - 2*sqrt(3+2^1.5)*T*sin(2*cf*pi*T)./ ...\n\t\texp(B*T))/2;\nA13 = -(2*T*cos(2*cf*pi*T)./exp(B*T) + 2*sqrt(3-2^1.5)*T*sin(2*cf*pi*T)./ ...\n\t\texp(B*T))/2;\nA14 = -(2*T*cos(2*cf*pi*T)./exp(B*T) - 2*sqrt(3-2^1.5)*T*sin(2*cf*pi*T)./ ...\n\t\texp(B*T))/2;\n\ngain = abs((-2*exp(4*i*cf*pi*T)*T + ...\n                 2*exp(-(B*T) + 2*i*cf*pi*T).*T.* ...\n                         (cos(2*cf*pi*T) - sqrt(3 - 2^(3/2))* ...\n                          sin(2*cf*pi*T))) .* ...\n           (-2*exp(4*i*cf*pi*T)*T + ...\n             2*exp(-(B*T) + 2*i*cf*pi*T).*T.* ...\n              (cos(2*cf*pi*T) + sqrt(3 - 2^(3/2)) * ...\n               sin(2*cf*pi*T))).* ...\n           (-2*exp(4*i*cf*pi*T)*T + ...\n             2*exp(-(B*T) + 2*i*cf*pi*T).*T.* ...\n              (cos(2*cf*pi*T) - ...\n               sqrt(3 + 2^(3/2))*sin(2*cf*pi*T))) .* ...\n           (-2*exp(4*i*cf*pi*T)*T + 2*exp(-(B*T) + 2*i*cf*pi*T).*T.* ...\n           (cos(2*cf*pi*T) + sqrt(3 + 2^(3/2))*sin(2*cf*pi*T))) ./ ...\n          (-2 ./ exp(2*B*T) - 2*exp(4*i*cf*pi*T) +  ...\n           2*(1 + exp(4*i*cf*pi*T))./exp(B*T)).^4);\n\t\nallfilts = ones(length(cf),1);\nfcoefs = [A0*allfilts A11 A12 A13 A14 A2*allfilts B0*allfilts B1 B2 gain];\n\nif (0)\t\t\t\t\t\t% Test Code\n\tA0  = fcoefs(:,1);\n\tA11 = fcoefs(:,2);\n\tA12 = fcoefs(:,3);\n\tA13 = fcoefs(:,4);\n\tA14 = fcoefs(:,5);\n\tA2  = fcoefs(:,6);\n\tB0  = fcoefs(:,7);\n\tB1  = fcoefs(:,8);\n\tB2  = fcoefs(:,9);\n\tgain= fcoefs(:,10);\t\n\tchan=1;\n\tx = [1 zeros(1, 511)];\n\ty1=filter([A0(chan)/gain(chan) A11(chan)/gain(chan) ...\n\t\tA2(chan)/gain(chan)],[B0(chan) B1(chan) B2(chan)], x);\n\ty2=filter([A0(chan) A12(chan) A2(chan)], ...\n\t\t\t[B0(chan) B1(chan) B2(chan)], y1);\n\ty3=filter([A0(chan) A13(chan) A2(chan)], ...\n\t\t\t[B0(chan) B1(chan) B2(chan)], y2);\n\ty4=filter([A0(chan) A14(chan) A2(chan)], ...\n\t\t\t[B0(chan) B1(chan) B2(chan)], y3);\n\tsemilogx((0:(length(x)-1))*(fs/length(x)),20*log10(abs(fft(y4))));\nend\n"
  },
  {
    "path": "auditory_toolkit/README",
    "content": "These files are the original auditory toolkit/gammatone filterbank code created\nby Malcolm Slaney and Dan Ellis, published at:\n\nhttp://labrosa.ee.columbia.edu/matlab/gammatonegram/\nhttps://engineering.purdue.edu/~malcolm/interval/1998-010/\n\nAny non-code assets (ie. the sample WAV file and associated graphs) have been\nremoved.\n"
  },
  {
    "path": "auditory_toolkit/demo_gammatone.m",
    "content": "%% Gammatone-like spectrograms\n% Gammatone filters are a popular linear approximation to the\n% filtering performed by the ear.  This routine provides a simple\n% wrapper for generating time-frequency surfaces based on a\n% gammatone analysis, which can be used as a replacement for a\n% conventional spectrogram.  It also provides a fast approximation\n% to this surface based on weighting the output of a conventional\n% FFT. \n\n%% Introduction\n% It is very natural to visualize sound as a time-varying\n% distribution of energy in frequency - not least because this is\n% one way of describing the information our brains get from our\n% ears via the auditory nerve.  The spectrogram is the traditional\n% time-frequency visualization, but it actually has some important\n% differences from how sound is analyzed by the ear, most\n% significantly that the ear's frequency subbands get wider for\n% higher frequencies, whereas the spectrogram has a constant\n% bandwidth across all frequency channels.\n% \n% There have been many signal-processing approximations proposed\n% for the frequency analysis performed by the ear; one of the most\n% popular is the Gammatone filterbank originally proposed by \n% Roy Patterson and colleagues in 1992.  Gammatone filters were \n% conceived as a simple fit to experimental observations of \n% the mammalian cochlea, and have a repeated pole structure leading\n% to an impulse response that is the product of a Gamma envelope \n% g(t) = t^n e^{-t} and a sinusoid (tone).\n%\n% One reason for the popularity of this approach is the\n% availability of an implementation by Malcolm Slaney, as \n% described in:\n%\n% Malcolm Slaney (1998) \"Auditory Toolbox Version 2\", \n% Technical Report #1998-010, Interval Research Corporation, 1998. \n% http://cobweb.ecn.purdue.edu/~malcolm/interval/1998-010/\n%\n% Malcolm's toolbox includes routines to design a Gammatone \n% filterbank and to process a signal by every filter in a bank, \n% but in order to convert this into a time-frequency visualization \n% it is necessary to sum up the energy within regular time bins.\n% While this is not complicated, the function here provides a \n% convenient wrapper to achieve this final step, for applications \n% that are content to work with time-frequency magnitude\n% distributions instead of going down to the waveform levels.  In\n% this mode of operation, the routine uses Malcolm's MakeERBFilters \n% and ERBFilterBank routines.\n%\n% This is, however, quite a computationally expensive approach, so\n% we also provide an alternative algorithm that gives very similar\n% results.  In this mode, the Gammatone-based spectrogram is\n% constructed by first calculating a conventional, fixed-bandwidth\n% spectrogram, then combining the fine frequency resolution of the\n% FFT-based spectra into the coarser, smoother Gammatone responses\n% via a weighting function.  This calculates the time-frequency\n% distribution some 30-40x faster than the full approach.\n\n%% Routines\n% The code consists of a main routine, <gammatonegram.m gammatonegram>, \n% which takes a waveform and other parameters and returns a\n% spectrogram-like time-frequency matrix, and a helper function \n% <fft2gammatonemx.m fft2gammatonemx>, which constructs the\n% weighting matrix to convert FFT output spectra into gammatone\n% approximations. \n\n%% Example usage\n% First, we calculate a Gammatone-based spectrogram-like image of \n% a speech waveform using the fast approximation.  Then we do the \n% same thing using the full filtering approach, for comparison.\n\n% Load a waveform, calculate its gammatone spectrogram, then display:\n[d,sr] = wavread('sa2.wav');\ntic; [D,F] = gammatonegram(d,sr); toc\n%Elapsed time is 0.140742 seconds.\nsubplot(211)\nimagesc(20*log10(D)); axis xy\ncaxis([-90 -30])\ncolorbar\n% F returns the center frequencies of each band;\n% display whichever elements were shown by the autoscaling\nset(gca,'YTickLabel',round(F(get(gca,'YTick'))));\nylabel('freq / Hz');\nxlabel('time / 10 ms steps');\ntitle('Gammatonegram - fast method')\n\n% Now repeat with flag to use actual subband filters.\n% Since it's the last argument, we have to include all the other\n% arguments.  These are the default values for: summation window \n% (0.025 sec), hop between successive windows (0.010 sec), \n% number of gammatone channels (64), lowest frequency (50 Hz), \n% and highest frequency (sr/2).  The last argument as zero \n% means not to use the FFT approach.\ntic; [D2,F2] = gammatonegram(d,sr,0.025,0.010,64,50,sr/2,0); toc\n%Elapsed time is 3.165083 seconds.\nsubplot(212)\nimagesc(20*log10(D2)); axis xy\ncaxis([-90 -30])\ncolorbar\nset(gca,'YTickLabel',round(F(get(gca,'YTick'))));\nylabel('freq / Hz');\nxlabel('time / 10 ms steps');\ntitle('Gammatonegram - accurate method')\n% Actual gammatone filters appear somewhat narrower.  The fast \n% version assumes coherence of addition of amplitude from \n% different channels, whereas the actual subband energies will\n% depend on how the energy in different frequencies combines.\n% Also notice the visible time smearing in the low frequency \n% channels that does not occur in the fast version.\n\n%% Validation\n% We can check the frequency responses of the filterbank \n% simulated with the fast method against the actual filters \n% from Malcolm's toolbox.  They match very closely, but of \n% course this still doesn't mean the two approaches will give \n% identical results - because the fast method ignores the phase \n% of each frequency channel when summing up.\n\n% Check the frequency responses to see that they match:\n% Put an impulse through the Slaney ERB filters, then take the \n% frequency response of each impulse response.\nfcfs = flipud(MakeERBFilters(16000,64,50));\ngtir = ERBFilterBank([1, zeros(1,1000)],fcfs);\nH = zeros(64,512);\nfor i = 1:64; H(i,:) = abs(freqz(gtir(i,:),1,512)); end\n% The weighting matrix for the FFT is the frequency response \n% of each output filter\ngtm = fft2gammatonemx(1024,16000,64,1,50,8000,512);\n% Plot every 5th channel from both.  Offset by 3 dB just so we can\n% see both\nfs = [0:511]/512*8000;\nfigure\nplot(fs,20*log10(H(5:5:64,:))','b',fs, -3 + 20*log10(gtm(5:5:64,:))','r')\naxis([0 8000 -150 0])\ngrid\n% Line up pretty well, apart from wiggles below -100 dB\n% (from truncating the impulse response at 1000 samples?)\n\n%% Download\n% You can download all the code and data for these examples here:\n% <gammatonegram.tgz gammatonegram.tgz>.\n\n%% Referencing\n% If you use this work in a publication, I would be grateful \n% if you referenced this page as follows:\n%\n% D. P. W. Ellis (2009).  \"Gammatone-like spectrograms\", web resource.\n% http://www.ee.columbia.edu/~dpwe/resources/matlab/gammatonegram/\n\n%% Acknowledgment\n% This project was supported in part by the NSF under \n% grant IIS-0535168. Any opinions, findings and conclusions \n% or recommendations expressed in this material are those of the \n% authors and do not necessarily reflect the views of the Sponsors.\n\n% Last updated: $Date: 2009/07/07 14:14:11 $\n% Dan Ellis <dpwe@ee.columbia.edu>\n"
  },
  {
    "path": "auditory_toolkit/fft2gammatonemx.m",
    "content": "function [wts,gain] = fft2gammatonemx(nfft, sr, nfilts, width, minfreq, maxfreq, maxlen)\n% wts = fft2gammatonemx(nfft, sr, nfilts, width, minfreq, maxfreq, maxlen)\n%      Generate a matrix of weights to combine FFT bins into\n%      Gammatone bins.  nfft defines the source FFT size at\n%      sampling rate sr.  Optional nfilts specifies the number of\n%      output bands required (default 64), and width is the\n%      constant width of each band in Bark (default 1).\n%      minfreq, maxfreq specify range covered in Hz (100, sr/2).\n%      While wts has nfft columns, the second half are all zero. \n%      Hence, aud spectrum is\n%      fft2gammatonemx(nfft,sr)*abs(fft(xincols,nfft));\n%      maxlen truncates the rows to this many bins\n%\n% 2004-09-05  Dan Ellis dpwe@ee.columbia.edu  based on rastamat/audspec.m\n% Last updated: $Date: 2009/02/22 02:29:25 $\n\nif nargin < 2;    sr = 16000; end\nif nargin < 3;    nfilts = 64; end\nif nargin < 4;    width = 1.0; end\nif nargin < 5;    minfreq = 100; end\nif nargin < 6;    maxfreq = sr/2; end\nif nargin < 7;    maxlen = nfft; end\n\nwts = zeros(nfilts, nfft);\n\n% after Slaney's MakeERBFilters\nEarQ = 9.26449;\nminBW = 24.7;\norder = 1;\n\ncfreqs = -(EarQ*minBW) + exp((1:nfilts)'*(-log(maxfreq + EarQ*minBW) + ...\n                log(minfreq + EarQ*minBW))/nfilts) * (maxfreq + EarQ*minBW);\ncfreqs = flipud(cfreqs);\n\nGTord = 4;\n\nucirc = exp(j*2*pi*[0:(nfft/2)]/nfft);\n\njustpoles = 0;\n\nfor i = 1:nfilts\n  cf = cfreqs(i);\n  ERB = width*((cf/EarQ).^order + minBW^order).^(1/order);\n  B = 1.019*2*pi*ERB;\n  r = exp(-B/sr);\n  theta = 2*pi*cf/sr;\n  pole = r*exp(j*theta);\n\n  if justpoles == 1\n    % point on unit circle of maximum gain, from differentiating magnitude\n    cosomegamax = (1+r*r)/(2*r)*cos(theta);\n    if abs(cosomegamax) > 1\n      if theta < pi/2;  omegamax = 0; \n      else              omegamax = pi;   end\n    else\n      omegamax = acos(cosomegamax);\n    end\n    center = exp(j*omegamax);\n    gain = abs((pole-center).*(pole'-center)).^GTord;\n    wts(i,1:(nfft/2+1)) = gain * (abs((pole-ucirc).*(pole'- ...\n                                                     ucirc)).^-GTord);\n  else\n    % poles and zeros, following Malcolm's MakeERBFilter\n    T = 1/sr;\n    A11 = -(2*T*cos(2*cf*pi*T)./exp(B*T) + 2*sqrt(3+2^1.5)*T*sin(2* ...\n                                                      cf*pi*T)./exp(B*T))/2; \n    A12 = -(2*T*cos(2*cf*pi*T)./exp(B*T) - 2*sqrt(3+2^1.5)*T*sin(2* ...\n                                                      cf*pi*T)./exp(B*T))/2;\n    A13 = -(2*T*cos(2*cf*pi*T)./exp(B*T) + 2*sqrt(3-2^1.5)*T*sin(2* ...\n                                                      cf*pi*T)./exp(B*T))/2; \n    A14 = -(2*T*cos(2*cf*pi*T)./exp(B*T) - 2*sqrt(3-2^1.5)*T*sin(2* ...\n                                                      cf*pi*T)./exp(B*T))/2; \n    zros = -[A11 A12 A13 A14]/T;\n    \n    gain(i) =  abs((-2*exp(4*j*cf*pi*T)*T + ...\n                2*exp(-(B*T) + 2*j*cf*pi*T).*T.* ...\n                (cos(2*cf*pi*T) - sqrt(3 - 2^(3/2))* ...\n                 sin(2*cf*pi*T))) .* ...\n               (-2*exp(4*j*cf*pi*T)*T + ...\n                2*exp(-(B*T) + 2*j*cf*pi*T).*T.* ...\n                (cos(2*cf*pi*T) + sqrt(3 - 2^(3/2)) * ...\n                 sin(2*cf*pi*T))).* ...\n               (-2*exp(4*j*cf*pi*T)*T + ...\n                2*exp(-(B*T) + 2*j*cf*pi*T).*T.* ...\n                (cos(2*cf*pi*T) - ...\n                 sqrt(3 + 2^(3/2))*sin(2*cf*pi*T))) .* ...\n               (-2*exp(4*j*cf*pi*T)*T + 2*exp(-(B*T) + 2*j*cf*pi*T).*T.* ...\n                (cos(2*cf*pi*T) + sqrt(3 + 2^(3/2))*sin(2*cf*pi*T))) ./ ...\n               (-2 ./ exp(2*B*T) - 2*exp(4*j*cf*pi*T) +  ...\n                2*(1 + exp(4*j*cf*pi*T))./exp(B*T)).^4);\n    wts(i,1:(nfft/2+1)) = ((T^4)/gain(i)) ...\n        * abs(ucirc-zros(1)).*abs(ucirc-zros(2))...\n        .*abs(ucirc-zros(3)).*abs(ucirc-zros(4))...\n        .*(abs((pole-ucirc).*(pole'-ucirc)).^-GTord);\n  end\nend\n\nwts = wts(:,1:maxlen);\n\n"
  },
  {
    "path": "auditory_toolkit/gammatone_demo.m",
    "content": "%% Gammatone-like spectrograms\n% Gammatone filters are a popular linear approximation to the\n% filtering performed by the ear.  This routine provides a simple\n% wrapper for generating time-frequency surfaces based on a\n% gammatone analysis, which can be used as a replacement for a\n% conventional spectrogram.  It also provides a fast approximation\n% to this surface based on weighting the output of a conventional\n% FFT. \n\n%% Introduction\n% It is very natural to visualize sound as a time-varying\n% distribution of energy in frequency - not least because this is\n% one way of describing the information our brains get from our\n% ears via the auditory nerve.  The spectrogram is the traditional\n% time-frequency visualization, but it actually has some important\n% differences from how sound is analyzed by the ear, most\n% significantly that the ear's frequency subbands get wider for\n% higher frequencies, whereas the spectrogram has a constant\n% bandwidth across all frequency channels.\n% \n% There have been many signal-processing approximations proposed\n% for the frequency analysis performed by the ear; one of the most\n% popular is the Gammatone filterbank originally proposed by \n% Roy Patterson and colleagues in 1992.  Gammatone filters were \n% conceived as a simple fit to experimental observations of \n% the mammalian cochlea, and have a repeated pole structure leading\n% to an impulse response that is the product of a Gamma envelope \n% g(t) = t^n e^{-t} and a sinusoid (tone).\n%\n% One reason for the popularity of this approach is the\n% availability of an implementation by Malcolm Slaney, as \n% described in:\n%\n% Malcolm Slaney (1998) \"Auditory Toolbox Version 2\", \n% Technical Report #1998-010, Interval Research Corporation, 1998. \n% http://cobweb.ecn.purdue.edu/~malcolm/interval/1998-010/\n%\n% Malcolm's toolbox includes routines to design a Gammatone \n% filterbank and to process a signal by every filter in a bank, \n% but in order to convert this into a time-frequency visualization \n% it is necessary to sum up the energy within regular time bins.\n% While this is not complicated, the function here provides a \n% convenient wrapper to achieve this final step, for applications \n% that are content to work with time-frequency magnitude\n% distributions instead of going down to the waveform levels.  In\n% this mode of operation, the routine uses Malcolm's MakeERBFilters \n% and ERBFilterBank routines.\n%\n% This is, however, quite a computationally expensive approach, so\n% we also provide an alternative algorithm that gives very similar\n% results.  In this mode, the Gammatone-based spectrogram is\n% constructed by first calculating a conventional, fixed-bandwidth\n% spectrogram, then combining the fine frequency resolution of the\n% FFT-based spectra into the coarser, smoother Gammatone responses\n% via a weighting function.  This calculates the time-frequency\n% distribution some 30-40x faster than the full approach.\n\n%% Routines\n% The code consists of a main routine, <gammatonegram.m gammatonegram>, \n% which takes a waveform and other parameters and returns a\n% spectrogram-like time-frequency matrix, and a helper function \n% <fft2gammatonemx.m fft2gammatonemx>, which constructs the\n% weighting matrix to convert FFT output spectra into gammatone\n% approximations. \n\n%% Example usage\n% First, we calculate a Gammatone-based spectrogram-like image of \n% a speech waveform using the fast approximation.  Then we do the \n% same thing using the full filtering approach, for comparison.\n\n% Load a waveform, calculate its gammatone spectrogram, then display:\n[d,sr] = wavread('sa2.wav');\ntic; D = gammatonegram(d,sr); toc\n%Elapsed time is 0.140742 seconds.\nsubplot(211)\nimagesc(20*log10(D)); axis xy\ncaxis([-90 -30])\ncolorbar\ntitle('Gammatonegram - fast method')\n\n% Now repeat with flag to use actual subband filters.\n% Since it's the last argument, we have to include all the other\n% arguments.  These are the default values for: summation window \n% (0.025 sec), hop between successive windows (0.010 sec), \n% number of gammatone channels (64), lowest frequency (50 Hz), \n% and highest frequency (sr/2).  The last argument as zero \n% means not to use the FFT approach.\ntic; D2 = gammatonegram(d,sr,0.025,0.010,64,50,sr/2,0); toc\n%Elapsed time is 3.165083 seconds.\nsubplot(212)\nimagesc(20*log10(D2)); axis xy\ncaxis([-90 -30])\ncolorbar\ntitle('Gammatonegram - accurate method')\n% Actual gammatone filters appear somewhat narrower.  The fast \n% version assumes coherence of addition of amplitude from \n% different channels, whereas the actual subband energies will\n% depend on how the energy in different frequencies combines.\n% Also notice the visible time smearing in the low frequency \n% channels that does not occur in the fast version.\n\n%% Validation\n% We can check the frequency responses of the filterbank \n% simulated with the fast method against the actual filters \n% from Malcolm's toolbox.  They match very closely, but of \n% course this still doesn't mean the two approaches will give \n% identical results - because the fast method ignores the phase \n% of each frequency channel when summing up.\n\n% Check the frequency responses to see that they match:\n% Put an impulse through the Slaney ERB filters, then take the \n% frequency response of each impulse response.\nfcfs = flipud(MakeERBFilters(16000,64,50));\ngtir = ERBFilterBank([1, zeros(1,1000)],fcfs);\nH = zeros(64,512);\nfor i = 1:64; H(i,:) = abs(freqz(gtir(i,:),1,512)); end\n% The weighting matrix for the FFT is the frequency response \n% of each output filter\ngtm = fft2gammatonemx(1024,16000,64,1,50,8000,512);\n% Plot every 5th channel from both.  Offset by 3 dB just so we can\n% see both\nfs = [0:511]/512*8000;\nfigure\nplot(fs,20*log10(H(5:5:64,:))','b',fs, -3 + 20*log10(gtm(5:5:64,:))','r')\naxis([0 8000 -150 0])\ngrid\n% Line up pretty well, apart from wiggles below -100 dB\n% (from truncating the impulse response at 1000 samples?)\n\n%% Download\n% You can download all the code and data for these examples here:\n% <gammatone.tgz gammatone.tgz>.\n\n%% Referencing\n% If you use this work in a publication, I would be grateful \n% if you referenced this page as follows:\n%\n%  D. P. W. Ellis (2009).  \"Gammatone-like spectrograms\", web resource, http://www.ee.columbia.edu/~dpwe/resources/matlab/gammatonegram/ .\n\n%% Acknowledgment\n% This project was supported in part by the NSF under \n% grant IIS-0535168. Any opinions, findings and conclusions \n% or recommendations expressed in this material are those of the \n% authors and do not necessarily reflect the views of the Sponsors.\n\n% Last updated: $Date: 2009/02/22 01:46:42 $\n% Dan Ellis <dpwe@ee.columbia.edu>\n"
  },
  {
    "path": "auditory_toolkit/gammatonegram.m",
    "content": "function [Y,F] = gammatonegram(X,SR,TWIN,THOP,N,FMIN,FMAX,USEFFT,WIDTH)\n% [Y,F] = gammatonegram(X,SR,N,TWIN,THOP,FMIN,FMAX,USEFFT,WIDTH)\n%    Calculate a spectrogram-like time frequency magnitude array\n%    based on Gammatone subband filters.  Waveform X (at sample\n%    rate SR) is passed through an N (default 64) channel gammatone \n%    auditory model filterbank, with lowest frequency FMIN (50) \n%    and highest frequency FMAX (SR/2).  The outputs of each band \n%    then have their energy integrated over windows of TWIN secs \n%    (0.025), advancing by THOP secs (0.010) for successive\n%    columns.  These magnitudes are returned as an N-row\n%    nonnegative real matrix, Y.\n%    If USEFFT is present and zero, revert to actual filtering and\n%    summing energy within windows.\n%    WIDTH (default 1.0) is how to scale bandwidth of filters \n%    relative to ERB default (for fast method only).\n%    F returns the center frequencies in Hz of each row of Y\n%    (uniformly spaced on a Bark scale).\n%\n% 2009-02-18 DAn Ellis dpwe@ee.columbia.edu\n% Last updated: $Date: 2009/02/23 21:07:09 $\n\nif nargin < 2;  SR = 16000; end\nif nargin < 3;  TWIN = 0.025; end\nif nargin < 4;  THOP = 0.010; end\nif nargin < 5;  N = 64; end\nif nargin < 6;  FMIN = 50; end\nif nargin < 7;  FMAX = SR/2; end\nif nargin < 8;  USEFFT = 1; end\nif nargin < 9;  WIDTH = 1.0; end\n\n\nif USEFFT == 0 \n\n  % Use malcolm's function to filter into subbands\n  %%%% IGNORES FMAX! *****\n  [fcoefs,F] = MakeERBFilters(SR, N, FMIN);\n  fcoefs = flipud(fcoefs);\n\n  XF = ERBFilterBank(X,fcoefs);\n\n  nwin = round(TWIN*SR);\n% Always use rectangular window for now\n%  if USEHANN == 1\n    window = hann(nwin)';\n%  else\n%    window = ones(1,nwin);\n%  end\n%  window = window/sum(window);\n%  XE = [zeros(N,round(nwin/2)),XF.^2,zeros(N,round(nwin/2))];\n  XE = [XF.^2];\n\n  hopsamps = round(THOP*SR);\n\n  ncols = 1 + floor((size(XE,2)-nwin)/hopsamps);\n\n  Y = zeros(N,ncols);\n\n%  winmx = repmat(window,N,1);\n\n  for i = 1:ncols\n%    Y(:,i) = sqrt(sum(winmx.*XE(:,(i-1)*hopsamps + [1:nwin]),2));\n    Y(:,i) = sqrt(mean(XE(:,(i-1)*hopsamps + [1:nwin]),2));\n  end\n\nelse \n  % USEFFT version\n  % How long a window to use relative to the integration window requested\n  winext = 1;\n  twinmod = winext * TWIN;\n  % first spectrogram\n  nfft = 2^(ceil(log(2*twinmod*SR)/log(2)));\n  nhop = round(THOP*SR);\n  nwin = round(twinmod*SR);\n  [gtm,F] = fft2gammatonemx(nfft, SR, N, WIDTH, FMIN, FMAX, nfft/2+1);\n  % perform FFT and weighting in amplitude domain\n  Y = 1/nfft*gtm*abs(specgram(X,nfft,SR,nwin,nwin-nhop));\n  % or the power domain?  doesn't match nearly as well\n  %Y = 1/nfft*sqrt(gtm*abs(specgram(X,nfft,SR,nwin,nwin-nhop).^2));\nend\n\n\n\n"
  },
  {
    "path": "auditory_toolkit/specgram.m",
    "content": "function y = specgram(x,n,sr,w,ov)\n% Y = myspecgram(X,NFFT,SR,W,OV)\n%      Substitute for Matlab's specgram, calculates & displays spectrogram\n% $Header: /homes/dpwe/tmp/e6820/RCS/myspecgram.m,v 1.1 2002/08/04 19:20:27 dpwe Exp $\n\nif (size(x,1) > size(x,2))\n  x = x';\nend\n\ns = length(x);\n\nif nargin < 2\n  n = 256;\nend\nif nargin < 3\n  sr = 1;\nend\nif nargin < 4\n  w = n;\nend\nif nargin < 5\n  ov = w/2;\nend\nh = w - ov;\n\nhalflen = w/2;\nhalff = n/2;   % midpoint of win\nacthalflen = min(halff, halflen);\n\nhalfwin = 0.5 * ( 1 + cos( pi * (0:halflen)/halflen));\nwin = zeros(1, n);\nwin((halff+1):(halff+acthalflen)) = halfwin(1:acthalflen);\nwin((halff+1):-1:(halff-acthalflen+2)) = halfwin(1:acthalflen);\n\nc = 1;\n\n% pre-allocate output array\nncols = 1+fix((s-n)/h);\nd = zeros((1+n/2), ncols);\n\nfor b = 0:h:(s-n)\n  u = win.*x((b+1):(b+n));\n  t = fft(u);\n  d(:,c) = t([1:(1+n/2)]');\n  c = c+1;\nend;\n\ntt = [0:h:(s-n)]/sr;\nff = [0:(n/2)]*sr/n;\n\nif nargout < 1\n  imagesc(tt,ff,20*log10(abs(d)));\n  axis xy\n  xlabel('Time / s');\n  ylabel('Frequency / Hz');\nelse\n  y = d;\nend\n"
  },
  {
    "path": "doc/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = _build\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\nclean:\n\t-rm -rf $(BUILDDIR)/*\n\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/gammatone.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/gammatone.qhc\"\n\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/gammatone\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/gammatone\"\n\t@echo \"# devhelp\"\n\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n"
  },
  {
    "path": "doc/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# gammatone documentation build configuration file, created by\n# sphinx-quickstart on Sat Dec  8 23:21:49 2012.\n#\n# This file is execfile()d with the current directory set to its containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys, os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#sys.path.insert(0, os.path.abspath('.'))\n\n# -- General configuration -----------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be extensions\n# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.\nextensions = ['sphinx.ext.autodoc']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'Gammatone Filterbank Toolkit'\ncopyright = u'2014, Jason Heeris'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '1.0'\n# The full version, including alpha/beta/rc tags.\nrelease = '1.0'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = ['_build']\n\n# The reST default role (used for this markup: `text`) to use for all documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n\n# -- Options for HTML output ---------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'haiku'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n#html_theme_path = []\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\nhtml_title = u\"%s %s\" % (project, release)\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\nhtml_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\nhtml_sidebars = {\n\t'**' : [\n\t\t'localtoc.html',\n\t\t'globaltoc.html',\n\t\t'relations.html',\n\t\t'searchbox.html'\n\t\t],\n\t}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\nhtml_show_sourcelink = False\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'gammatonedoc'\n\n\n# -- Options for LaTeX output --------------------------------------------------\n\nlatex_elements = {\n# The paper size ('letterpaper' or 'a4paper').\n#'papersize': 'letterpaper',\n\n# The font size ('10pt', '11pt' or '12pt').\n#'pointsize': '10pt',\n\n# Additional stuff for the LaTeX preamble.\n#'preamble': '',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title, author, documentclass [howto/manual]).\nlatex_documents = [\n  ('index', 'gammatone.tex', u'Gammatone Documentation',\n   u'Jason Heeris', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output --------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'gammatone', u'Gammatone Documentation',\n     [u'Jason Heeris'], 1)\n]\n\n# If true, show URL addresses after external links.\n#man_show_urls = False\n\n\n# -- Options for Texinfo output ------------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n  ('index', 'gammatone', u'Gammatone Documentation',\n   u'Jason Heeris', 'gammatone', 'Gammatone filterbank construction tools.',\n   'Miscellaneous'),\n]\n\n# Documents to append as an appendix to all manuals.\n#texinfo_appendices = []\n\n# If false, no module index is generated.\n#texinfo_domain_indices = True\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n#texinfo_show_urls = 'footnote'\n\n# -- Autodoc configuration -----------------------------------------------------\n\n# autodoc_default_flags = ['members']\n"
  },
  {
    "path": "doc/details.rst",
    "content": "About the Gammatone Filterbank Toolkit\n--------------------------------------\n\nSummary\n~~~~~~~\n\nThis is a port of Malcolm Slaney's and Dan Ellis' gammatone filterbank\nMATLAB code, detailed below, to Python 2 and 3 using Numpy and Scipy. It\nanalyses signals by running them through banks of gammatone filters,\nsimilar to Fourier-based spectrogram analysis.\n\n.. figure:: FurElise.png\n   :align: center\n   :alt: Gammatone-based spectrogram of Für Elise\n\n   Gammatone-based spectrogram of Für Elise\n\nDependencies\n~~~~~~~~~~~~\n\n-  numpy\n-  scipy\n-  nose\n-  mock\n-  matplotlib\n\nUsing the Code\n~~~~~~~~~~~~~~\n\nFor a demonstration, find a `.wav` file (for example,\n`Für Elise <http://heeris.id.au/samples/FurElise.wav>`_) and run::\n\n    python -m gammatone FurElise.wav -d 10\n\n...to see a gammatone-gram of the first ten seconds of Beethoven's \"Für\nElise.\" If you've installed via\n``pip`` or ``setup.py install``, you should also be able to just run::\n\n    gammatone FurElise.wav -d 10\n\nBasis\n~~~~~\n\nThis project is based on research into how humans perceive audio,\noriginally published by Malcolm Slaney:\n\n`Malcolm Slaney (1998) \"Auditory Toolbox Version 2\", Technical Report\n#1998-010, Interval Research Corporation,\n1998. <http://cobweb.ecn.purdue.edu/~malcolm/interval/1998-010/>`_\n\nSlaney's report describes a way of modelling how the human ear\nperceives, emphasises and separates different frequencies of sound. A\nseries of gammatone filters are constructed whose width increases with\nincreasing centre frequency, and this bank of filters is applied to a\ntime-domain signal. The result of this is a spectrum that should\nrepresent the human experience of sound better than, say, a\nFourier-domain spectrum would.\n\nA gammatone filter has an impulse response that is a sine wave\nmultiplied by a gamma distribution function. It is a common approach to\nmodelling the auditory system.\n\nThe gammatone filterbank approach can be considered analogous (but not\nequivalent) to a discrete Fourier transform where the frequency axis is\nlogarithmic. For example, a series of notes spaced an octave apart would\nappear to be roughly linearly spaced; or a sound that was distributed\nacross the same linear frequency range would appear to have more spread\nat lower frequencies.\n\nThe real goal of this toolkit is to allow easy computation of the\ngammatone equivalent of a spectrogram — a time-varying spectrum of\nenergy over audible frequencies based on a gammatone filterbank.\n\nSlaney demonstrated his research with an initial implementation in\nMATLAB. This implementation was later extended by Dan Ellis, who found a\nway to approximate a \"gammatone-gram\" by using the fast Fourier\ntransform. Ellis' code calculates a matrix of weights that can be\napplied to the output of a FFT so that a Fourier-based spectrogram can\neasily be transformed into such an approximation.\n\nEllis' code and documentation is here: `Gammatone-like\nspectrograms <http://labrosa.ee.columbia.edu/matlab/gammatonegram/>`_\n\nInterest\n~~~~~~~~\n\nI became interested in this because of my background in science\ncommunication and my general interest in the teaching of signal\nprocessing. I find that the spectrogram approach to visualising signals\nis adequate for illustrating abstract systems or the mathematical\nproperties of transforms, but bears little correspondence to a person's\nown experience of sound. If someone wants to see what their favourite\npiece of music \"looks like,\" a normal Fourier transform based\nspectrogram is actually quite a poor way to visualise it. Features of\nthe audio seem to be oddly spaced or unnaturally emphasised or\nde-emphasised depending on where they are in the frequency domain.\n\nThe gammatone filterbank approach seems to be closer to what someone\nmight intuitively expect a visualisation of sound to look like, and can\nhelp develop an intuition about alternative representations of signals.\n\nVerifying the port\n~~~~~~~~~~~~~~~~~~\n\nSince this is a port of existing MATLAB code, I've written tests to\nverify the Python implementation against the original code. These tests\naren't unit tests, but they do generally test single functions. Running\nthe tests has the same workflow:\n\n1. Run the scripts in the ``test_generation`` directory. This will\n   create a ``.mat`` file containing test data in ``tests/data``.\n\n2. Run ``nosetest3`` in the top level directory. This will find and run\n   all the tests in the ``tests`` directory.\n\nAlthough I'm usually loathe to check in generated files to version\ncontrol, I'm willing to make an exception for the ``.mat`` files\ncontaining the test data. My reasoning is that they represent the\ndecoupling of my code from the MATLAB code, and if the two projects were\nseparated, they would be considered a part of the Python code, not the\noriginal MATLAB code.\n"
  },
  {
    "path": "doc/fftweight.rst",
    "content": ":mod:`gammatone.fftweight` -- FFT weightings for spectrogram-like gammatone analysis\n====================================================================================\n\n.. automodule:: gammatone.fftweight\n   :members:\n"
  },
  {
    "path": "doc/filters.rst",
    "content": ":mod:`gammatone.filters` -- gammatone filterbank construction\n=============================================================\n\n.. automodule:: gammatone.filters\n   :members:\n"
  },
  {
    "path": "doc/gtgram.rst",
    "content": ":mod:`gammatone.gtgram` -- spectrogram-like gammatone analysis\n==============================================================\n\n.. automodule:: gammatone.gtgram\n   :members:\n"
  },
  {
    "path": "doc/index.rst",
    "content": ".. gammatone documentation master file, created by\n   sphinx-quickstart on Sat Dec  8 23:21:49 2012.\n\nIndex\n=====\n\nModules\n-------\n\n.. toctree::\n   :maxdepth: 2\n\n   filters\n   gtgram\n   fftweight\n   plot\n\n.. include:: details.rst\n   \nIndices and tables\n------------------\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n\n"
  },
  {
    "path": "doc/make.bat",
    "content": "@ECHO OFF\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset BUILDDIR=_build\nset ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .\nset I18NSPHINXOPTS=%SPHINXOPTS% .\nif NOT \"%PAPER%\" == \"\" (\n\tset ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%\n\tset I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%\n)\n\nif \"%1\" == \"\" goto help\n\nif \"%1\" == \"help\" (\n\t:help\n\techo.Please use `make ^<target^>` where ^<target^> is one of\n\techo.  html       to make standalone HTML files\n\techo.  dirhtml    to make HTML files named index.html in directories\n\techo.  singlehtml to make a single large HTML file\n\techo.  pickle     to make pickle files\n\techo.  json       to make JSON files\n\techo.  htmlhelp   to make HTML files and a HTML help project\n\techo.  qthelp     to make HTML files and a qthelp project\n\techo.  devhelp    to make HTML files and a Devhelp project\n\techo.  epub       to make an epub\n\techo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\n\techo.  text       to make text files\n\techo.  man        to make manual pages\n\techo.  texinfo    to make Texinfo files\n\techo.  gettext    to make PO message catalogs\n\techo.  changes    to make an overview over all changed/added/deprecated items\n\techo.  linkcheck  to check all external links for integrity\n\techo.  doctest    to run all doctests embedded in the documentation if enabled\n\tgoto end\n)\n\nif \"%1\" == \"clean\" (\n\tfor /d %%i in (%BUILDDIR%\\*) do rmdir /q /s %%i\n\tdel /q /s %BUILDDIR%\\*\n\tgoto end\n)\n\nif \"%1\" == \"html\" (\n\t%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/html.\n\tgoto end\n)\n\nif \"%1\" == \"dirhtml\" (\n\t%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.\n\tgoto end\n)\n\nif \"%1\" == \"singlehtml\" (\n\t%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.\n\tgoto end\n)\n\nif \"%1\" == \"pickle\" (\n\t%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can process the pickle files.\n\tgoto end\n)\n\nif \"%1\" == \"json\" (\n\t%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can process the JSON files.\n\tgoto end\n)\n\nif \"%1\" == \"htmlhelp\" (\n\t%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can run HTML Help Workshop with the ^\n.hhp project file in %BUILDDIR%/htmlhelp.\n\tgoto end\n)\n\nif \"%1\" == \"qthelp\" (\n\t%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can run \"qcollectiongenerator\" with the ^\n.qhcp project file in %BUILDDIR%/qthelp, like this:\n\techo.^> qcollectiongenerator %BUILDDIR%\\qthelp\\gammatone.qhcp\n\techo.To view the help file:\n\techo.^> assistant -collectionFile %BUILDDIR%\\qthelp\\gammatone.ghc\n\tgoto end\n)\n\nif \"%1\" == \"devhelp\" (\n\t%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished.\n\tgoto end\n)\n\nif \"%1\" == \"epub\" (\n\t%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The epub file is in %BUILDDIR%/epub.\n\tgoto end\n)\n\nif \"%1\" == \"latex\" (\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; the LaTeX files are in %BUILDDIR%/latex.\n\tgoto end\n)\n\nif \"%1\" == \"text\" (\n\t%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The text files are in %BUILDDIR%/text.\n\tgoto end\n)\n\nif \"%1\" == \"man\" (\n\t%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The manual pages are in %BUILDDIR%/man.\n\tgoto end\n)\n\nif \"%1\" == \"texinfo\" (\n\t%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.\n\tgoto end\n)\n\nif \"%1\" == \"gettext\" (\n\t%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The message catalogs are in %BUILDDIR%/locale.\n\tgoto end\n)\n\nif \"%1\" == \"changes\" (\n\t%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.The overview file is in %BUILDDIR%/changes.\n\tgoto end\n)\n\nif \"%1\" == \"linkcheck\" (\n\t%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Link check complete; look for any errors in the above output ^\nor in %BUILDDIR%/linkcheck/output.txt.\n\tgoto end\n)\n\nif \"%1\" == \"doctest\" (\n\t%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Testing of doctests in the sources finished, look at the ^\nresults in %BUILDDIR%/doctest/output.txt.\n\tgoto end\n)\n\n:end\n"
  },
  {
    "path": "doc/plot.rst",
    "content": ":mod:`gammatone.plot` -- Plotting utilities for gammatone analysis\n==================================================================\n\n.. automodule:: gammatone.plot\n   :members:\n"
  },
  {
    "path": "gammatone/__init__.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\n\n# Designate gammatone module\n\"\"\"\nGammatone filterbank toolkit\n\"\"\"\n"
  },
  {
    "path": "gammatone/__main__.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfrom gammatone.plot import main\nmain()\n"
  },
  {
    "path": "gammatone/fftweight.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\n\"\"\"\nThis module contains functions for calculating weights to approximate a\ngammatone filterbank-like \"spectrogram\" from a Fourier transform.\n\"\"\"\nfrom __future__ import division\nimport numpy as np\n\nimport gammatone.filters as filters\nimport gammatone.gtgram as gtgram\n\ndef specgram_window(\n        nfft,\n        nwin,\n    ):\n    \"\"\"\n    Window calculation used in specgram replacement function. Hann window of\n    width `nwin` centred in an array of width `nfft`.\n    \"\"\"\n    halflen = nwin // 2\n    halff = nfft // 2 # midpoint of win\n    acthalflen = int(np.floor(min(halff, halflen)))\n    halfwin = 0.5 * ( 1 + np.cos(np.pi * np.arange(0, halflen+1)/halflen))\n    win = np.zeros((nfft,))\n    win[halff:halff+acthalflen] = halfwin[0:acthalflen];\n    win[halff:halff-acthalflen:-1] = halfwin[0:acthalflen];\n    return win\n\n\ndef specgram(x, n, sr, w, h):\n    \"\"\" Substitute for Matlab's specgram, calculates a simple spectrogram.\n\n    :param x: The signal to analyse\n    :param n: The FFT length\n    :param sr: The sampling rate\n    :param w: The window length (see :func:`specgram_window`)\n    :param h: The hop size (must be greater than zero)\n    \"\"\"\n    # Based on Dan Ellis' myspecgram.m,v 1.1 2002/08/04\n    assert h > 0, \"Must have a hop size greater than 0\"\n\n    s = x.shape[0]\n    win = specgram_window(n, w)\n\n    c = 0\n\n    # pre-allocate output array\n    ncols = 1 + int(np.floor((s - n)/h))\n    d = np.zeros(((1 + n // 2), ncols), np.dtype(complex))\n\n    for b in range(0, s - n, h):\n      u = win * x[b : b + n]\n      t = np.fft.fft(u)\n      d[:, c] = t[0 : (1 + n // 2)].T\n      c = c + 1\n\n    return d\n\n\ndef fft_weights(\n    nfft,\n    fs,\n    nfilts,\n    width,\n    fmin,\n    fmax,\n    maxlen):\n    \"\"\"\n    :param nfft: the source FFT size\n    :param sr: sampling rate (Hz)\n    :param nfilts: the number of output bands required (default 64)\n    :param width: the constant width of each band in Bark (default 1)\n    :param fmin: lower limit of frequencies (Hz)\n    :param fmax: upper limit of frequencies (Hz)\n    :param maxlen: number of bins to truncate the rows to\n    \n    :return: a tuple `weights`, `gain` with the calculated weight matrices and\n             gain vectors\n    \n    Generate a matrix of weights to combine FFT bins into Gammatone bins.\n    \n    Note about `maxlen` parameter: While wts has nfft columns, the second half\n    are all zero. Hence, aud spectrum is::\n    \n        fft2gammatonemx(nfft,sr)*abs(fft(xincols,nfft))\n    \n    `maxlen` truncates the rows to this many bins.\n    \n    | (c) 2004-2009 Dan Ellis dpwe@ee.columbia.edu  based on rastamat/audspec.m\n    | (c) 2012 Jason Heeris (Python implementation)\n    \"\"\"\n    ucirc = np.exp(1j * 2 * np.pi * np.arange(0, nfft / 2 + 1) / nfft)[None, ...]\n    \n    # Common ERB filter code factored out\n    cf_array = filters.erb_space(fmin, fmax, nfilts)[::-1]\n\n    _, A11, A12, A13, A14, _, _, _, B2, gain = (\n        filters.make_erb_filters(fs, cf_array, width).T\n    )\n    \n    A11, A12, A13, A14 = A11[..., None], A12[..., None], A13[..., None], A14[..., None]\n\n    r = np.sqrt(B2)\n    theta = 2 * np.pi * cf_array / fs    \n    pole = (r * np.exp(1j * theta))[..., None]\n    \n    GTord = 4\n    \n    weights = np.zeros((nfilts, nfft))\n\n    weights[:, 0:ucirc.shape[1]] = (\n          np.abs(ucirc + A11 * fs) * np.abs(ucirc + A12 * fs)\n        * np.abs(ucirc + A13 * fs) * np.abs(ucirc + A14 * fs)\n        * np.abs(fs * (pole - ucirc) * (pole.conj() - ucirc)) ** (-GTord)\n        / gain[..., None]\n    )\n\n    weights = weights[:, 0:int(maxlen)]\n\n    return weights, gain\n\n\ndef fft_gtgram(\n    wave,\n    fs,\n    window_time, hop_time,\n    channels,\n    f_min):\n    \"\"\"\n    Calculate a spectrogram-like time frequency magnitude array based on\n    an FFT-based approximation to gammatone subband filters.\n\n    A matrix of weightings is calculated (using :func:`gtgram.fft_weights`), and\n    applied to the FFT of the input signal (``wave``, using sample rate ``fs``).\n    The result is an approximation of full filtering using an ERB gammatone\n    filterbank (as per :func:`gtgram.gtgram`).\n\n    ``f_min`` determines the frequency cutoff for the corresponding gammatone\n    filterbank. ``window_time`` and ``hop_time`` (both in seconds) are the size\n    and overlap of the spectrogram columns.\n\n    | 2009-02-23 Dan Ellis dpwe@ee.columbia.edu\n    |\n    | (c) 2013 Jason Heeris (Python implementation)\n    \"\"\"\n    width = 1 # Was a parameter in the MATLAB code\n\n    nfft = int(2 ** (np.ceil(np.log2(2 * window_time * fs))))\n    nwin, nhop, _ = gtgram.gtgram_strides(fs, window_time, hop_time, 0);\n\n    gt_weights, _ = fft_weights(\n            nfft,\n            fs,\n            channels,\n            width,\n            f_min,\n            fs / 2,\n            nfft / 2 + 1\n        )\n\n    sgram = specgram(wave, nfft, fs, nwin, nhop)\n\n    result = gt_weights.dot(np.abs(sgram)) / nfft\n\n    return result\n"
  },
  {
    "path": "gammatone/filters.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\n\"\"\"\nThis module contains functions for constructing sets of equivalent rectangular\nbandwidth gammatone filters.\n\"\"\"\nfrom __future__ import division\nfrom collections import namedtuple\n\nimport numpy as np\nimport scipy as sp\nfrom scipy import signal as sgn\n\nDEFAULT_FILTER_NUM = 100\nDEFAULT_LOW_FREQ = 100\nDEFAULT_HIGH_FREQ = 44100 / 4\n\n\ndef erb_point(low_freq, high_freq, fraction):\n    \"\"\"\n    Calculates a single point on an ERB scale between ``low_freq`` and\n    ``high_freq``, determined by ``fraction``. When ``fraction`` is ``1``,\n    ``low_freq`` will be returned. When ``fraction`` is ``0``, ``high_freq``\n    will be returned.\n    \n    ``fraction`` can actually be outside the range ``[0, 1]``, which in general\n    isn't very meaningful, but might be useful when ``fraction`` is rounded a\n    little above or below ``[0, 1]`` (eg. for plot axis labels).\n    \"\"\"\n    # Change the following three parameters if you wish to use a different ERB\n    # scale. Must change in MakeERBCoeffs too.\n    # TODO: Factor these parameters out\n    ear_q = 9.26449 # Glasberg and Moore Parameters\n    min_bw = 24.7\n    order = 1\n\n    # All of the following expressions are derived in Apple TR #35, \"An\n    # Efficient Implementation of the Patterson-Holdsworth Cochlear Filter\n    # Bank.\" See pages 33-34.\n    erb_point = (\n        -ear_q * min_bw\n        + np.exp(\n            fraction * (\n                -np.log(high_freq + ear_q * min_bw)\n                + np.log(low_freq + ear_q * min_bw)\n                )\n        ) *\n        (high_freq + ear_q * min_bw)\n    )\n    \n    return erb_point\n\n\ndef erb_space(\n    low_freq=DEFAULT_LOW_FREQ,\n    high_freq=DEFAULT_HIGH_FREQ,\n    num=DEFAULT_FILTER_NUM):\n    \"\"\"\n    This function computes an array of ``num`` frequencies uniformly spaced\n    between ``high_freq`` and ``low_freq`` on an ERB scale.\n    \n    For a definition of ERB, see Moore, B. C. J., and Glasberg, B. R. (1983).\n    \"Suggested formulae for calculating auditory-filter bandwidths and\n    excitation patterns,\" J. Acoust. Soc. Am. 74, 750-753.\n    \"\"\"\n    return erb_point(\n        low_freq,\n        high_freq,\n        np.arange(1, num + 1) / num\n        )\n\n\ndef centre_freqs(fs, num_freqs, cutoff):\n    \"\"\"\n    Calculates an array of centre frequencies (for :func:`make_erb_filters`)\n    from a sampling frequency, lower cutoff frequency and the desired number of\n    filters.\n    \n    :param fs: sampling rate\n    :param num_freqs: number of centre frequencies to calculate\n    :type num_freqs: int\n    :param cutoff: lower cutoff frequency\n    :return: same as :func:`erb_space`\n    \"\"\"\n    return erb_space(cutoff, fs / 2, num_freqs)\n\n\ndef make_erb_filters(fs, centre_freqs, width=1.0):\n    \"\"\"\n    This function computes the filter coefficients for a bank of \n    Gammatone filters. These filters were defined by Patterson and Holdworth for\n    simulating the cochlea. \n    \n    The result is returned as a :class:`ERBCoeffArray`. Each row of the\n    filter arrays contains the coefficients for four second order filters. The\n    transfer function for these four filters share the same denominator (poles)\n    but have different numerators (zeros). All of these coefficients are\n    assembled into one vector that the ERBFilterBank can take apart to implement\n    the filter.\n    \n    The filter bank contains \"numChannels\" channels that extend from\n    half the sampling rate (fs) to \"lowFreq\". Alternatively, if the numChannels\n    input argument is a vector, then the values of this vector are taken to be\n    the center frequency of each desired filter. (The lowFreq argument is\n    ignored in this case.)\n    \n    Note this implementation fixes a problem in the original code by\n    computing four separate second order filters. This avoids a big problem with\n    round off errors in cases of very small cfs (100Hz) and large sample rates\n    (44kHz). The problem is caused by roundoff error when a number of poles are\n    combined, all very close to the unit circle. Small errors in the eigth order\n    coefficient, are multiplied when the eigth root is taken to give the pole\n    location. These small errors lead to poles outside the unit circle and\n    instability. Thanks to Julius Smith for leading me to the proper\n    explanation.\n    \n    Execute the following code to evaluate the frequency response of a 10\n    channel filterbank::\n    \n        fcoefs = MakeERBFilters(16000,10,100);\n        y = ERBFilterBank([1 zeros(1,511)], fcoefs);\n        resp = 20*log10(abs(fft(y')));\n        freqScale = (0:511)/512*16000;\n        semilogx(freqScale(1:255),resp(1:255,:));\n        axis([100 16000 -60 0])\n        xlabel('Frequency (Hz)'); ylabel('Filter Response (dB)');\n    \n    | Rewritten by Malcolm Slaney@Interval.  June 11, 1998.\n    | (c) 1998 Interval Research Corporation\n    |\n    | (c) 2012 Jason Heeris (Python implementation)\n    \"\"\"\n    T = 1 / fs\n    # Change the followFreqing three parameters if you wish to use a different\n    # ERB scale. Must change in ERBSpace too.\n    # TODO: factor these out\n    ear_q = 9.26449 # Glasberg and Moore Parameters\n    min_bw = 24.7\n    order = 1\n\n    erb = width*((centre_freqs / ear_q) ** order + min_bw ** order) ** ( 1 /order)\n    B = 1.019 * 2 * np.pi * erb\n\n    arg = 2 * centre_freqs * np.pi * T\n    vec = np.exp(2j * arg)\n\n    A0 = T\n    A2 = 0\n    B0 = 1\n    B1 = -2 * np.cos(arg) / np.exp(B * T)\n    B2 = np.exp(-2 * B * T)\n    \n    rt_pos = np.sqrt(3 + 2 ** 1.5)\n    rt_neg = np.sqrt(3 - 2 ** 1.5)\n    \n    common = -T * np.exp(-(B * T))\n    \n    # TODO: This could be simplified to a matrix calculation involving the\n    # constant first term and the alternating rt_pos/rt_neg and +/-1 second\n    # terms\n    k11 = np.cos(arg) + rt_pos * np.sin(arg)\n    k12 = np.cos(arg) - rt_pos * np.sin(arg)\n    k13 = np.cos(arg) + rt_neg * np.sin(arg)\n    k14 = np.cos(arg) - rt_neg * np.sin(arg)\n\n    A11 = common * k11\n    A12 = common * k12\n    A13 = common * k13\n    A14 = common * k14\n\n    gain_arg = np.exp(1j * arg - B * T)\n\n    gain = np.abs(\n            (vec - gain_arg * k11)\n          * (vec - gain_arg * k12)\n          * (vec - gain_arg * k13)\n          * (vec - gain_arg * k14)\n          * (  T * np.exp(B * T)\n             / (-1 / np.exp(B * T) + 1 + vec * (1 - np.exp(B * T)))\n            )**4\n        )\n\n    allfilts = np.ones_like(centre_freqs)\n    \n    fcoefs = np.column_stack([\n        A0 * allfilts, A11, A12, A13, A14, A2*allfilts,\n        B0 * allfilts, B1, B2,\n        gain\n    ])\n    \n    return fcoefs\n\n\ndef erb_filterbank(wave, coefs):\n    \"\"\"\n    :param wave: input data (one dimensional sequence)\n    :param coefs: gammatone filter coefficients\n    \n    Process an input waveform with a gammatone filter bank. This function takes\n    a single sound vector, and returns an array of filter outputs, one channel\n    per row.\n    \n    The fcoefs parameter, which completely specifies the Gammatone filterbank,\n    should be designed with the :func:`make_erb_filters` function.\n    \n    | Malcolm Slaney @ Interval, June 11, 1998.\n    | (c) 1998 Interval Research Corporation\n    | Thanks to Alain de Cheveigne' for his suggestions and improvements.\n    |\n    | (c) 2013 Jason Heeris (Python implementation)\n    \"\"\"\n    output = np.zeros((coefs[:,9].shape[0], wave.shape[0]))\n    \n    gain = coefs[:, 9]\n    # A0, A11, A2\n    As1 = coefs[:, (0, 1, 5)]\n    # A0, A12, A2\n    As2 = coefs[:, (0, 2, 5)]\n    # A0, A13, A2\n    As3 = coefs[:, (0, 3, 5)]\n    # A0, A14, A2\n    As4 = coefs[:, (0, 4, 5)]\n    # B0, B1, B2\n    Bs = coefs[:, 6:9]\n    \n    # Loop over channels\n    for idx in range(0, coefs.shape[0]):\n        # These seem to be reversed (in the sense of A/B order), but that's what\n        # the original code did...\n        # Replacing these with polynomial multiplications reduces both accuracy\n        # and speed.\n        y1 = sgn.lfilter(As1[idx], Bs[idx], wave)\n        y2 = sgn.lfilter(As2[idx], Bs[idx], y1)\n        y3 = sgn.lfilter(As3[idx], Bs[idx], y2)\n        y4 = sgn.lfilter(As4[idx], Bs[idx], y3)\n        output[idx, :] = y4 / gain[idx]\n        \n    return output\n"
  },
  {
    "path": "gammatone/gtgram.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfrom __future__ import division\nimport numpy as np\n\nfrom .filters import make_erb_filters, centre_freqs, erb_filterbank\n\n\"\"\"\nThis module contains functions for rendering \"spectrograms\" which use gammatone\nfilterbanks instead of Fourier transforms.\n\"\"\"\n\ndef round_half_away_from_zero(num):\n    \"\"\" Implement the round-half-away-from-zero rule, where fractional parts of\n    0.5 result in rounding up to the nearest positive integer for positive\n    numbers, and down to the nearest negative number for negative integers.\n    \"\"\"\n    return np.sign(num) * np.floor(np.abs(num) + 0.5)\n\n\ndef gtgram_strides(fs, window_time, hop_time, filterbank_cols):\n    \"\"\"\n    Calculates the window size for a gammatonegram.\n    \n    @return a tuple of (window_size, hop_samples, output_columns)\n    \"\"\"\n    nwin        = int(round_half_away_from_zero(window_time * fs))\n    hop_samples = int(round_half_away_from_zero(hop_time * fs))\n    columns     = (1\n                    + int(\n                        np.floor(\n                            (filterbank_cols - nwin)\n                            / hop_samples\n                        )\n                    )\n                  )\n        \n    return (nwin, hop_samples, columns)\n\n\ndef gtgram_xe(wave, fs, channels, f_min):\n    \"\"\" Calculate the intermediate ERB filterbank processed matrix \"\"\"\n    cfs = centre_freqs(fs, channels, f_min)\n    fcoefs = np.flipud(make_erb_filters(fs, cfs))\n    xf = erb_filterbank(wave, fcoefs)\n    xe = np.power(xf, 2)\n    return xe\n\n\ndef gtgram(\n    wave,\n    fs,\n    window_time, hop_time,\n    channels,\n    f_min):\n    \"\"\"\n    Calculate a spectrogram-like time frequency magnitude array based on\n    gammatone subband filters. The waveform ``wave`` (at sample rate ``fs``) is\n    passed through an multi-channel gammatone auditory model filterbank, with\n    lowest frequency ``f_min`` and highest frequency ``f_max``. The outputs of\n    each band then have their energy integrated over windows of ``window_time``\n    seconds, advancing by ``hop_time`` secs for successive columns. These\n    magnitudes are returned as a nonnegative real matrix with ``channels`` rows.\n    \n    | 2009-02-23 Dan Ellis dpwe@ee.columbia.edu\n    |\n    | (c) 2013 Jason Heeris (Python implementation)\n    \"\"\"\n    xe = gtgram_xe(wave, fs, channels, f_min)    \n    \n    nwin, hop_samples, ncols = gtgram_strides(\n        fs,\n        window_time,\n        hop_time,\n        xe.shape[1]\n    )\n    \n    y = np.zeros((channels, ncols))\n    \n    for cnum in range(ncols):\n        segment = xe[:, cnum * hop_samples + np.arange(nwin)]\n        y[:, cnum] = np.sqrt(segment.mean(1))\n    \n    return y\n"
  },
  {
    "path": "gammatone/plot.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n#\n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\n\"\"\"\nPlotting utilities related to gammatone analysis, primarily for use with\n``matplotlib``.\n\"\"\"\nfrom __future__ import division\nimport argparse\nimport os.path\n\nimport matplotlib.pyplot\nimport matplotlib.ticker\nimport numpy as np\nimport scipy.constants\nimport scipy.io.wavfile\n\nfrom .filters import erb_point\nimport gammatone.gtgram\nimport gammatone.fftweight\n\n\nclass ERBFormatter(matplotlib.ticker.EngFormatter):\n    \"\"\"\n    Axis formatter for gammatone filterbank analysis. This formatter calculates\n    the ERB spaced frequencies used for analysis, and renders them similarly to\n    the engineering axis formatter.\n\n    The scale is changed so that `[0, 1]` corresponds to ERB spaced frequencies\n    from ``high_freq`` to ``low_freq`` (note the reversal). It should be used\n    with ``imshow`` where the ``extent`` argument is ``[a, b, 1, 0]`` (again,\n    note the inversion).\n    \"\"\"\n\n    def __init__(self, low_freq, high_freq, *args, **kwargs):\n        \"\"\"\n        Creates a new :class ERBFormatter: for use with ``matplotlib`` plots.\n        Note that this class does not supply the ``units`` or ``places``\n        arguments; typically these would be ``'Hz'`` and ``0``.\n\n        :param low_freq: the low end of the gammatone filterbank frequency range\n        :param high_freq: the high end of the gammatone filterbank frequency\n          range\n        \"\"\"\n        self.low_freq = low_freq\n        self.high_freq = high_freq\n        super().__init__(*args, **kwargs)\n\n    def _erb_axis_scale(self, fraction):\n        return erb_point(self.low_freq, self.high_freq, fraction)\n\n    def __call__(self, val, pos=None):\n        newval = self._erb_axis_scale(val)\n        return super().__call__(newval, pos)\n\n\ndef gtgram_plot(\n        gtgram_function,\n        axes, x, fs,\n        window_time, hop_time, channels, f_min,\n        imshow_args=None\n        ):\n    \"\"\"\n    Plots a spectrogram-like time frequency magnitude array based on gammatone\n    subband filters.\n\n    :param gtgram_function: A function with signature::\n\n        fft_gtgram(\n            wave,\n            fs,\n            window_time, hop_time,\n            channels,\n            f_min)\n\n    See :func:`gammatone.gtgram.gtgram` for details of the paramters.\n    \"\"\"\n    # Set a nice formatter for the y-axis\n    formatter = ERBFormatter(f_min, fs/2, unit='Hz', places=0)\n    axes.yaxis.set_major_formatter(formatter)\n\n    # Figure out time axis scaling\n    duration = len(x) / fs\n\n    # Calculate 1:1 aspect ratio\n    aspect_ratio = duration/scipy.constants.golden\n\n    gtg = gtgram_function(x, fs, window_time, hop_time, channels, f_min)\n    Z = np.flipud(20 * np.log10(gtg))\n\n    img = axes.imshow(Z, extent=[0, duration, 1, 0], aspect=aspect_ratio)\n\n\n# Entry point for CLI script\n\nHELP_TEXT = \"\"\"\\\nPlots the gammatone filterbank analysis of a WAV file.\n\nIf the file contains more than one channel, all channels are averaged before\nperforming analysis.\n\"\"\"\n\n\ndef render_audio_from_file(path, duration, function):\n    \"\"\"\n    Renders the given ``duration`` of audio from the audio file at ``path``\n    using the gammatone spectrogram function ``function``.\n    \"\"\"\n    samplerate, data = scipy.io.wavfile.read(path)\n\n    # Average the stereo signal\n    if duration:\n        nframes = duration * samplerate\n        data = data[0 : nframes, :]\n\n    signal = data.mean(1)\n\n    # Default gammatone-based spectrogram parameters\n    twin = 0.08\n    thop = twin / 2\n    channels = 1024\n    fmin = 20\n\n    # Set up the plot\n    fig = matplotlib.pyplot.figure()\n    axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])\n\n    gtgram_plot(\n        function,\n        axes,\n        signal,\n        samplerate,\n        twin, thop, channels, fmin)\n\n    axes.set_title(os.path.basename(path))\n    axes.set_xlabel(\"Time (s)\")\n    axes.set_ylabel(\"Frequency\")\n\n    matplotlib.pyplot.show()\n\n\ndef main():\n    \"\"\"\n    Entry point for CLI application to plot gammatonegrams of sound files.\n    \"\"\"\n    parser = argparse.ArgumentParser(description=HELP_TEXT)\n\n    parser.add_argument(\n        'sound_file',\n        help=\"The sound file to graph. See the help text for supported formats.\")\n\n    parser.add_argument(\n        '-d', '--duration', type=int,\n        help=\"The time in seconds from the start of the audio to use for the \"\n             \"graph (default is to use the whole file).\"\n        )\n\n    parser.add_argument(\n        '-a', '--accurate', action='store_const', dest='function',\n        const=gammatone.gtgram.gtgram, default=gammatone.fftweight.fft_gtgram,\n        help=\"Use the full filterbank approach instead of the weighted FFT \"\n             \"approximation. This is much slower, and uses a lot of memory, but\"\n             \" is more accurate.\"\n        )\n\n    args = parser.parse_args()\n\n    return render_audio_from_file(args.sound_file, args.duration, args.function)\n"
  },
  {
    "path": "setup.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n#\n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfrom setuptools import setup, find_packages\n\nsetup(\n    name = \"Gammatone\",\n    version = \"1.0\",\n    packages = find_packages(),\n\n    install_requires = [\n        'numpy',\n        'scipy',\n        'nose',\n        'mock',\n        'matplotlib',\n    ],\n\n    entry_points = {\n        'console_scripts': [\n            'gammatone = gammatone.plot:main',\n        ]\n    }\n)\n"
  },
  {
    "path": "test_generation/README",
    "content": "These are Octave/MATLAB scripts that create test data for the Python\nimplementation of that gammatone library.\n\nYou must add both this directory and the top level 'auditory_toolkit' directory\nto your search path.\n\nThe scripts are designed to run under MATLAB and Octave (using '--traditional').\n"
  },
  {
    "path": "test_generation/test_ERBFilterBank.m",
    "content": "% Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n% \n% This file is part of the gammatone toolkit, and is licensed under the 3-clause\n% BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfunction test_ERBFilterBank()\n\n    erb_space_inputs = { ...\n        100, 11025,  10, sin(2*pi*220*[0:22050/100]'/22050); ...\n         20, 22050,  10, square(2*pi*150*[0:44100/200]'/44100); ...\n         20, 44100,  40, square(2*pi*12000*[0:88200/400]'/88200); ...\n        100, 11025, 1000, sawtooth(2*pi*10100*[0:22050/100]'/22050, 0.5); ...\n        500, 80000,  200, sawtooth(2*pi*3333*[0:160000/400]'/160000, 0.5); ...\n    };\n    \n    erb_filter_inputs = { ...\n        44100, [22050; 2205; 220], square(2*pi*220*[0:44100/200]'/44100); ...\n        16000, [8000; 7000; 6000; 5000; 4000; 3000; 2000; 1000], square(2*pi*2000*[0:16000/50]'/16000); ...\n        16000, [16000; 8000; 1], square(2*pi*880*[0:16000/50]'/16000); ...\n    };\n    \n    num_tests = size(erb_space_inputs)(1) ...\n                + size(erb_filter_inputs)(1);\n    \n    erb_filterbank_inputs = {};\n    \n    erb_filterbank_results = {};\n    \n    % This will ONLY generate tests that use the centre frequency inputs\n    \n    % ERBSpace generated inputs\n    for tnum=1:size(erb_space_inputs)(1)\n        [f_low, f_high, num_f, wave] = deal(erb_space_inputs{tnum,:});\n        fs = f_high*2;\n        f_arr = ERBSpace(f_low, f_high, num_f);\n        fcoefs = MakeERBFilters(fs, f_arr, 0);\n        erb_filterbank_inputs(tnum, :) = {fcoefs, wave};\n    end\n    \n    % MakeERBFilters generated inputs\n    for tnum=1:size(erb_filter_inputs)\n        [fs, f_arr, wave] = deal(erb_filter_inputs{tnum,:});\n        fcoefs = MakeERBFilters(fs, f_arr, 0);\n        offset = size(erb_space_inputs)(1);\n        erb_filterbank_inputs(offset+tnum, :) = {fcoefs, wave};\n    end\n    \n    for tnum=1:num_tests\n        fcoefs = erb_filterbank_inputs{tnum, 1};\n        wave = erb_filterbank_inputs{tnum, 2};\n        erb_filterbank_results(tnum, :) = ERBFilterBank(wave, fcoefs);\n    end\n\n    results_file = fullfile('..', 'tests', 'data', 'test_filterbank_data.mat');\n    save(results_file, 'erb_filterbank_inputs', 'erb_filterbank_results');\nend\n"
  },
  {
    "path": "test_generation/test_ERBSpace.m",
    "content": "% Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n% \n% This file is part of the gammatone toolkit, and is licensed under the 3-clause\n% BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfunction test_ERBSpace()\n    \n    % Low freq, high freq, N\n    erbspace_inputs = { ...\n        100, 11025,  100; ...\n        100, 22050,  100; ...\n         20, 22050,  100; ...\n         20, 44100,  100; ...\n        100, 11025,   10; ...\n        100, 11025, 1000; ...\n        500, 80000,  200; ...\n    };\n    \n    erbspace_results = {};\n    \n    num_tests = size(erbspace_inputs)(1);\n    \n    for tnum=1:num_tests\n        [f_low, f_high, num_f] = deal(erbspace_inputs{tnum,:});\n        erbspace_results(tnum, :) = ERBSpace(f_low, f_high, num_f);\n    end\n    \n    results_file = fullfile('..', 'tests', 'data', 'test_erbspace_data.mat');\n    save(results_file, 'erbspace_inputs', 'erbspace_results');\nend\n"
  },
  {
    "path": "test_generation/test_MakeERBFilters.m",
    "content": "% Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n% \n% This file is part of the gammatone toolkit, and is licensed under the 3-clause\n% BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfunction test_MakeERBFilters()\n    \n    erb_space_inputs = { ...\n        100, 11025,  100; ...\n        100, 22050,  100; ...\n         20, 22050,  100; ...\n         20, 44100,  100; ...\n        100, 11025,   10; ...\n        100, 11025, 1000; ...\n        500, 80000,  200; ...\n    };\n    \n    extra_inputs = { ...\n        44100, [22050; 2205; 220]; ...\n        16000, [8000; 7000; 6000; 5000; 4000; 3000; 2000; 1000]; ...\n        16000, [16000; 8000; 1]; ...\n    };\n     \n    num_tests = size(erb_space_inputs)(1) + size(extra_inputs)(1);\n    \n    erb_filter_inputs = {};\n    \n    erb_filter_results = {};\n    \n    % This will ONLY generate tests that use the centre frequency inputs\n    \n    % ERBSpace generated inputs\n    for tnum=1:size(erb_space_inputs)(1)\n        [f_low, f_high, num_f] = deal(erb_space_inputs{tnum,:});\n        fs = f_high*2;\n        cfs = ERBSpace(f_low, f_high, num_f);\n        erb_filter_inputs(tnum, :) = {fs, cfs};\n    end\n    \n    erb_filter_inputs = cat(1, erb_filter_inputs, extra_inputs);\n    \n    for tnum=1:num_tests\n        fs = erb_filter_inputs{tnum, 1};\n        cfs = erb_filter_inputs{tnum, 2};\n        fcoefs = MakeERBFilters(fs, cfs, 0);\n        erb_filter_results(tnum, :) = fcoefs;\n    end\n\n    results_file = fullfile('..', 'tests', 'data', 'test_erb_filter_data.mat');\n    save(results_file, 'erb_filter_inputs', 'erb_filter_results');\nend\n"
  },
  {
    "path": "test_generation/test_fft2gammatonemx.m",
    "content": "% Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n% \n% This file is part of the gammatone toolkit, and is licensed under the 3-clause\n% BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfunction test_fft2gtmx()\n    % Arguments:\n    % nfft, sr, nfilts, width, minfreq, maxfreq, maxlen\n    \n    fft2gtmx_inputs = { ...\n        256 , 48000, 64 , 1   , 100, 48000/2 , 256; ...\n        % Vary the width parameter\n        256 , 48000, 64 , 2   , 100, 48000/2 , 256; ...\n        256 , 48000, 64 , 4   , 100, 48000/2 , 256; ...\n        256 , 48000, 64 , 0.25, 100, 48000/2 , 256; ...\n        % Vary sampling rate\n        256 , 96000, 64 , 1   , 100, 96000/2 , 256; ...\n        % Vary upper frequency\n        256 , 48000, 64 , 1   , 100, 48000/2 , 256; ...\n        256 , 48000, 64 , 1   , 100, 48000/4 , 256; ...\n        256 , 48000, 64 , 1   , 100, 48000/10, 256; ...\n        % Vary maxlen\n        256 , 48000, 64 , 1   , 100, 48000/2 , 128; ...\n        256 , 48000, 64 , 1   , 100, 48000/2 , 16; ...\n        256 , 48000, 64 , 1   , 100, 48000/2 , 99; ...\n        % Vary sampling rate\n        1024, 48000, 128, 1   , 100, 48000/2 , 512; ...\n        1024, 48000, 128, 1   , 100, 48000/2 , 128; ...\n        64  , 44100, 32 , 1   , 20 , 44100/2 , 64; ...\n    };\n    \n    fft2gtmx_results = {};\n    \n    for tnum=1:size(fft2gtmx_inputs)(1)\n        [nfft, sr, nfilts, width, minfreq, maxfreq, maxlen] = deal(fft2gtmx_inputs{tnum,:});\n        [wts, gain] = fft2gammatonemx(nfft, sr, nfilts, width, minfreq, maxfreq, maxlen);\n        fft2gtmx_results(tnum, :) = {wts, gain};\n    end\n    \n    results_file = fullfile('..', 'tests', 'data', 'test_fft2gtmx_data.mat');\n    save(results_file, 'fft2gtmx_inputs', 'fft2gtmx_results');\nend\n"
  },
  {
    "path": "test_generation/test_fft_gammatonegram.m",
    "content": "% Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n% \n% This file is part of the gammatone toolkit, and is licensed under the 3-clause\n% BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfunction test_fft_gammatonegram()\n    % Need:\n    %  wave\n    %  fs\n    %  window_time\n    %  hop_time\n    %  channels\n    %  f_min\n    %  f_max\n    \n    % Need to mock out:\n    %  make_erb_filters output (elide)\n    %  centre_freqs (elide)\n    %  erb_filterbank (depends on X, SR, N, FMIN)\n    \n    % Ensure reproducible tests\n    rand('state', [3 1 4 1 5 9 2 7]);\n    \n    fft_gammatonegram_inputs = {\n        'sawtooth_01', sawtooth(2*pi*10100*[0:22050 - 1]'/22050, 0.5), 22050, 0.025, 0.010, 64, 50; ...\n        'sin220_01'  , sin(2*pi*220*[0:4800 - 1]'/48000), 48000, 0.01, 0.01, 64, 50; ...\n        'sin220_02'  , sin(2*pi*220*[0:4800 - 1]'/48000), 48000, 0.025, 0.01, 32, 50; ...\n        'rand_01'    , rand([1, 4410 - 1]), 44100, 0.02, 0.015, 128, 500; ...\n        'rand_02'    , rand([1, 9600 - 1]), 96000, 0.01, 0.005, 256, 20; ...\n        'rand_03'    , rand([1, 4800 - 1]), 48000, 0.01, 0.010, 256, 20; ...\n    };\n    \n    % Mocked intermediate results for unit testing\n    fft_gammatonegram_mocks = {};\n    \n    % Actual results\n    fft_gammatonegram_results = {};\n    \n    for tnum=1:size(fft_gammatonegram_inputs)(1)\n        [name, wave, fs, twin, thop, chs, fmin] = deal(fft_gammatonegram_inputs{tnum,:});\n\n        % This is for mocking the output of the equivalent Python functions\n        nfft = 2^(ceil(log(2*twin*fs)/log(2)));\n        nwin = round(twin * fs);    \n        nhop = round(thop * fs);\n        \n        % Mock out the FFT weights as well\n        wts = fft2gammatonemx( ...\n            nfft, ...\n            fs, ...\n            chs, ...\n            1, ... % width is always 1 in the Python implementation\n            fmin, ...\n            fs/2, ...\n            nfft/2+1 ...\n        );\n\n        % Mock out windowing function\n        window = gtgram_window(nfft, nwin);\n\n        res = gammatonegram( ...\n            wave, ...\n            fs, ...\n            twin, ...\n            thop, ...\n            chs, ...\n            fmin, ...\n            fs/2, % fmax is always fs/2 in the Python version\n            1     % Use FFT method\n        );\n        \n        fft_gammatonegram_mocks(tnum, :) = { ...\n            wts ...\n        };\n    \n        fft_gammatonegram_results(tnum, :) = { ...\n            res, ...\n            window, ...\n            nfft, ...\n            nwin, ...\n            nhop ...\n        };\n    \n    end;\n    \n    results_file = fullfile('..', 'tests', 'data', 'test_fft_gammatonegram_data.mat');\n    save(results_file, 'fft_gammatonegram_inputs', 'fft_gammatonegram_mocks', 'fft_gammatonegram_results');\nend;\n\n\nfunction win = gtgram_window(n, w)\n    % Reproduction of Dan Ellis' windowing function built in to specgram.m\n    halflen = w/2;\n    halff = n/2;   % midpoint of win\n    acthalflen = min(halff, halflen);\n\n    halfwin = 0.5 * ( 1 + cos( pi * (0:halflen)/halflen));\n    win = zeros(1, n);\n    win((halff+1):(halff+acthalflen)) = halfwin(1:acthalflen);\n    win((halff+1):-1:(halff-acthalflen+2)) = halfwin(1:acthalflen);\nend;"
  },
  {
    "path": "test_generation/test_gammatonegram.m",
    "content": "% Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n% \n% This file is part of the gammatone toolkit, and is licensed under the 3-clause\n% BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfunction test_gammatonegram()\n    % Need:\n    %  wave\n    %  fs\n    %  window_time\n    %  hop_time\n    %  channels\n    %  f_min\n    %  f_max\n    \n    % Need to mock out:\n    %  make_erb_filters output (elide)\n    %  centre_freqs (elide)\n    %  erb_filterbank (depends on X, SR, N, FMIN)\n    \n    % Ensure reproducible tests\n    rand('state', [3 1 4 1 5 9 2 7]);\n    \n    gammatonegram_inputs = {\n        'sawtooth_01', sawtooth(2*pi*10100*[0:22050 - 1]'/22050, 0.5), 22050, 0.025, 0.010, 64, 50; ...\n        'sin220_01'  , sin(2*pi*220*[0:4800 - 1]'/48000), 48000, 0.01, 0.01, 64, 50; ...\n        'sin220_02'  , sin(2*pi*220*[0:4800 - 1]'/48000), 48000, 0.025, 0.01, 32, 50; ...\n        'rand_01'    , rand([1, 4410 - 1]), 44100, 0.02, 0.015, 128, 500; ...\n        'rand_02'    , rand([1, 9600 - 1]), 96000, 0.01, 0.005, 256, 20; ...\n        'rand_03'    , rand([1, 4800 - 1]), 48000, 0.01, 0.010, 256, 20; ...\n    };\n    \n    % Mocked intermediate results for unit testing\n    gammatonegram_mocks = {};\n    \n    % Actual results\n    gammatonegram_results = {};\n    \n    for tnum=1:size(gammatonegram_inputs)(1)\n        [name, wave, fs, twin, thop, chs, fmin] = deal(gammatonegram_inputs{tnum,:});\n        res = gammatonegram( ...\n                  wave, ...\n                  fs, ...\n                  twin, ...\n                  thop, ...\n                  chs, ...\n                  fmin, ...\n                  0, % fmax is ignored\n                  0 % Don't use FFT method\n              );\n    \n        % This is for mocking the output of the equivalent Python functions\n        nwin     = round(twin * fs);    \n        hopsamps = round(thop * fs);\n        f_coefs  = flipud(MakeERBFilters(fs, chs, fmin));\n        x_f      = ERBFilterBank(wave, f_coefs);\n        x_e      = [x_f .^ 2];\n        x_e_cols = size(x_e, 2);\n        ncols    = 1 + floor((x_e_cols - nwin) / hopsamps);\n       \n        % Mock out the ERB filter functions too\n        fcoefs = flipud(MakeERBFilters(fs, chs, fmin));\n        erb_fb_output = ERBFilterBank(wave, fcoefs);\n    \n        gammatonegram_mocks(tnum, :) = { ...\n            erb_fb_output, ...\n            x_e_cols ...\n        };\n    \n        gammatonegram_results(tnum, :) = { ...\n            res, ...\n            nwin, ...\n            hopsamps, ...\n            ncols ...\n        };\n    \n    end;\n    \n    results_file = fullfile('..', 'tests', 'data', 'test_gammatonegram_data.mat');\n    save(results_file, 'gammatonegram_inputs', 'gammatonegram_mocks', 'gammatonegram_results');\nend;\n"
  },
  {
    "path": "test_generation/test_specgram.m",
    "content": "% Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n% \n% This file is part of the gammatone toolkit, and is licensed under the 3-clause\n% BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfunction test_specgram()\n    % Need:\n    %  wave\n    %  nfft\n    %  fs\n    %  window_size\n    %  hop (technically the function takes the overlap, but only to recalculate this)\n    \n    % Ensure reproducible tests\n    rand('state', [3 1 4 1 5 9 2 7]);\n    \n    specgram_inputs = {\n        'sawtooth_01', sawtooth(2*pi*10100*[0:22050 - 1]'/22050, 0.5), 2048, 22050, 551, 221; ...\n        'sin220_01'  , sin(2*pi*220*[0:4800 - 1]'/48000), 1024, 48000, 480, 480; ...\n        'sin220_02'  , sin(2*pi*220*[0:4800 - 1]'/48000), 4096, 48000, 1200, 480; ...\n        'rand_01'    , rand([1, 4410 - 1]), 2048, 44100, 882, 662; ...\n        'rand_02'    , rand([1, 9600 - 1]), 2048, 96000, 960, 480; ...\n        'rand_03'    , rand([1, 4800 - 1]), 1024, 48000, 480, 480; ...\n    };\n    \n    % Mocked intermediate results for unit testing\n    specgram_mocks = {};\n    \n    % Actual results\n    specgram_results = {};\n    \n    for tnum=1:size(specgram_inputs)(1)\n        [name, wave, nfft, fs, nwin, nhop] = deal(specgram_inputs{tnum,:});\n\n        % Mock out windowing function\n        window = gtgram_window(nfft, nwin);\n\n        res = specgram( ...\n            wave, ...\n            nfft, ...\n            fs, ...\n            nwin, ...\n            nwin - nhop ...\n        );\n        \n        specgram_mocks(tnum, :) = { ...\n            window, ...\n        };\n    \n        specgram_results(tnum, :) = { ...\n            res, ...\n        };\n    \n    end;\n    \n    results_file = fullfile('..', 'tests', 'data', 'test_specgram_data.mat');\n    save(results_file, 'specgram_inputs', 'specgram_mocks', 'specgram_results');\nend;\n\n\nfunction win = gtgram_window(n, w)\n    % Reproduction of Dan Ellis' windowing function built in to specgram.m\n    halflen = w/2;\n    halff = n/2;   % midpoint of win\n    acthalflen = min(halff, halflen);\n\n    halfwin = 0.5 * ( 1 + cos( pi * (0:halflen)/halflen));\n    win = zeros(1, n);\n    win((halff+1):(halff+acthalflen)) = halfwin(1:acthalflen);\n    win((halff+1):-1:(halff-acthalflen+2)) = halfwin(1:acthalflen);\nend;"
  },
  {
    "path": "tests/__init__.py",
    "content": "# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\n\n# Designate as module\n"
  },
  {
    "path": "tests/test_cfs.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nimport nose\nfrom mock import patch\n\nimport gammatone.filters\n\nEXPECTED_PARAMS = (\n    ((0, 0, 0), (0, 0, 0)),\n    ((22050, 100, 100), (100, 11025, 100)),\n    ((44100, 100, 100), (100, 22050, 100)),\n    ((44100, 100, 20), (20, 22050, 100)),\n    ((88200, 100, 20), (20, 44100, 100)),\n    ((22050, 100, 10), (10, 11025, 100)),\n    ((22050, 1000, 100), (100, 11025, 1000)),\n    ((160000, 500, 200), (200, 80000, 500)),\n)\n\n\ndef test_centre_freqs():\n    for args, params in EXPECTED_PARAMS:\n        yield CentreFreqsTester(args, params)\n\n\nclass CentreFreqsTester:\n\n    def __init__(self, args, params):\n        self.args = args\n        self.params = params\n        self.description = \"Centre freqs for {:g} {:d} {:g}\".format(*args)\n\n\n    @patch('gammatone.filters.erb_space')\n    def __call__(self, erb_space_mock):\n        gammatone.filters.centre_freqs(*self.args)\n        erb_space_mock.assert_called_with(*self.params)\n\n\nif __name__ == '__main__':\n    nose.main()\n"
  },
  {
    "path": "tests/test_erb_space.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nimport nose\nimport numpy as np\nimport scipy.io\nfrom pkg_resources import resource_stream\n\nimport gammatone.filters\n\nREF_DATA_FILENAME = 'data/test_erbspace_data.mat'\n\nINPUT_KEY  = 'erbspace_inputs'\nRESULT_KEY = 'erbspace_results'\n\nINPUT_COLS  = ('f_low', 'f_high', 'num_f')\nRESULT_COLS = ('cfs',)\n\n\ndef load_reference_data():\n    \"\"\" Load test data generated from the reference code \"\"\"\n    # Load test data\n    with resource_stream(__name__, REF_DATA_FILENAME) as test_data:\n        data = scipy.io.loadmat(test_data, squeeze_me=False)\n    \n    zipped_data = zip(data[INPUT_KEY], data[RESULT_KEY])\n    \n    for inputs, refs in zipped_data:\n        input_dict = dict(zip(INPUT_COLS, map(np.squeeze, inputs)))\n        ref_dict = dict(zip(RESULT_COLS, map(np.squeeze, refs)))\n        yield (input_dict, ref_dict)\n    \n\ndef test_ERB_space_known_values():\n    for inputs, refs in load_reference_data():\n        args = (\n            inputs['f_low'],\n            inputs['f_high'],\n            inputs['num_f'],\n        )\n        \n        expected = (refs['cfs'],)\n        \n        yield ERBSpaceTester(args, expected)\n\n\nclass ERBSpaceTester:\n    \n    def __init__(self, args, expected):\n        self.args = args\n        self.expected = expected[0]\n        self.description = (\n            \"ERB space for {:.1f} {:.1f} {:d}\".format(\n                float(self.args[0]),\n                float(self.args[1]),\n                int(self.args[2]),\n            )\n        )\n    \n    def __call__(self):\n        result = gammatone.filters.erb_space(*self.args)\n        assert np.allclose(result, self.expected, rtol=1e-6, atol=1e-10)\n\nif __name__ == '__main__':\n    nose.main()\n"
  },
  {
    "path": "tests/test_fft_gtgram.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n#\n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfrom mock import patch\nimport nose\nimport numpy as np\nimport scipy.io\nfrom pkg_resources import resource_stream\n\nimport gammatone.fftweight\n\nREF_DATA_FILENAME = 'data/test_fft_gammatonegram_data.mat'\n\nINPUT_KEY  = 'fft_gammatonegram_inputs'\nMOCK_KEY   = 'fft_gammatonegram_mocks'\nRESULT_KEY = 'fft_gammatonegram_results'\n\nINPUT_COLS  = ('name', 'wave', 'fs', 'twin', 'thop', 'channels', 'fmin')\nMOCK_COLS   = ('wts',)\nRESULT_COLS = ('res', 'window', 'nfft', 'nwin', 'nhop')\n\n\ndef load_reference_data():\n    \"\"\" Load test data generated from the reference code \"\"\"\n    # Load test data\n    with resource_stream(__name__, REF_DATA_FILENAME) as test_data:\n        data = scipy.io.loadmat(test_data, squeeze_me=False)\n\n    zipped_data = zip(data[INPUT_KEY], data[MOCK_KEY], data[RESULT_KEY])\n    for inputs, mocks, refs in zipped_data:\n        input_dict = dict(zip(INPUT_COLS, inputs))\n        mock_dict  = dict(zip(MOCK_COLS, mocks))\n        ref_dict = dict(zip(RESULT_COLS, refs))\n\n        yield (input_dict, mock_dict, ref_dict)\n\n\ndef test_fft_specgram_window():\n    for inputs, mocks, refs in load_reference_data():\n        args = (\n            refs['nfft'],\n            refs['nwin'],\n        )\n\n        expected = (\n            refs['window'],\n        )\n\n        yield FFTGtgramWindowTester(inputs['name'], args, expected)\n\nclass FFTGtgramWindowTester:\n\n    def __init__(self, name, args, expected):\n        self.nfft = args[0].squeeze()\n        self.nwin = args[1].squeeze()\n        self.expected = expected[0].squeeze()\n\n        self.description = (\n            \"FFT gammatonegram window for nfft = {:f}, nwin = {:f}\".format(\n                float(self.nfft), float(self.nwin)\n            ))\n\n    def __call__(self):\n        result = gammatone.fftweight.specgram_window(self.nfft, self.nwin)\n        max_diff = np.max(np.abs(result - self.expected))\n        diagnostic = \"Maximum difference: {:6e}\".format(max_diff)\n        assert np.allclose(result, self.expected, rtol=1e-6, atol=1e-12), diagnostic\n\n\ndef test_fft_gtgram():\n    for inputs, mocks, refs in load_reference_data():\n        args = (\n            inputs['fs'],\n            inputs['twin'],\n            inputs['thop'],\n            inputs['channels'],\n            inputs['fmin']\n        )\n\n        yield FFTGammatonegramTester(\n            inputs['name'][0],\n            args,\n            inputs['wave'],\n            mocks['wts'],\n            refs['window'],\n            refs['res']\n        )\n\nclass FFTGammatonegramTester:\n    \"\"\" Testing class for gammatonegram calculation \"\"\"\n\n    def __init__(self, name, args, sig, fft_weights, window, expected):\n        self.signal = np.asarray(sig).squeeze()\n        self.expected = np.asarray(expected).squeeze()\n        self.fft_weights = np.asarray(fft_weights)\n        self.args = args\n        self.window = window.squeeze()\n\n        self.description = \"FFT gammatonegram for {:s}\".format(name)\n\n    def __call__(self):\n        # Note that the second return value from fft_weights isn't actually used\n        with patch(\n                'gammatone.fftweight.fft_weights',\n                return_value=(self.fft_weights, None)), \\\n            patch(\n                'gammatone.fftweight.specgram_window',\n                return_value=self.window):\n\n            result = gammatone.fftweight.fft_gtgram(self.signal, *self.args)\n\n            max_diff = np.max(np.abs(result - self.expected))\n            diagnostic = \"Maximum difference: {:6e}\".format(max_diff)\n\n            assert np.allclose(result, self.expected, rtol=1e-6, atol=1e-12), diagnostic\n\nif __name__ == '__main__':\n    nose.main()\n"
  },
  {
    "path": "tests/test_fft_weights.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfrom __future__ import division\nimport nose\nimport numpy as np\nimport scipy.io\nfrom pkg_resources import resource_stream\n\nimport gammatone.fftweight\n\nREF_DATA_FILENAME = 'data/test_fft2gtmx_data.mat'\n\nINPUT_KEY  = 'fft2gtmx_inputs'\nRESULT_KEY = 'fft2gtmx_results'\n\nINPUT_COLS  = ('nfft', 'sr', 'nfilts', 'width', 'fmin', 'fmax', 'maxlen')\nRESULT_COLS = ('weights', 'gain',)\n\ndef load_reference_data():\n    \"\"\" Load test data generated from the reference code \"\"\"\n    # Load test data\n    with resource_stream(__name__, REF_DATA_FILENAME) as test_data:\n        data = scipy.io.loadmat(test_data, squeeze_me=False)\n    \n    zipped_data = zip(data[INPUT_KEY], data[RESULT_KEY])\n    \n    for inputs, refs in zipped_data:\n        input_dict = dict(zip(INPUT_COLS, map(np.squeeze, inputs)))\n        ref_dict = dict(zip(RESULT_COLS, map(np.squeeze, refs)))\n        yield (input_dict, ref_dict)\n\n\ndef fft_weights_funcs(args, expected):\n    \"\"\"\n    Construct a pair of unit tests for the gains and weights of the FFT to\n    gammatonegram calculation. Returns two functions: test_gains, test_weights.\n    \"\"\"\n    args = list(args)\n    expected_weights = expected[0]\n    expected_gains = expected[1]\n    \n    # Convert nfft, nfilts, maxlen to ints\n    args[0] = int(args[0])\n    args[2] = int(args[2])\n    args[6] = int(args[6])\n    \n    weights, gains = gammatone.fftweight.fft_weights(*args)\n    \n    (test_weights_desc, test_gains_desc) = (\n        \"FFT weights {:s} for nfft = {:d}, fs = {:d}, nfilts = {:d}\".format(\n            label,\n            int(args[0]),\n            int(args[1]),\n            int(args[2]),\n    ) for label in (\"weights\", \"gains\"))\n    \n    def test_gains():\n        assert gains.shape == expected_gains.shape \n        assert np.allclose(gains, expected_gains, rtol=1e-6, atol=1e-12)\n \n    def test_weights():\n        assert weights.shape == expected_weights.shape\n        assert np.allclose(weights, expected_weights, rtol=1e-6, atol=1e-12)\n \n    test_gains.description = test_gains_desc\n    test_weights.description = test_weights_desc\n    \n    return test_gains, test_weights\n\n\ndef test_fft_weights():\n    for inputs, refs in load_reference_data():\n        args = tuple(inputs[col] for col in INPUT_COLS)        \n        expected = (refs['weights'], refs['gain'])\n        test_gains, test_weights = fft_weights_funcs(args, expected)\n        yield test_gains\n        yield test_weights\n\n\nif __name__ == '__main__':\n    nose.main()\n"
  },
  {
    "path": "tests/test_filterbank.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nimport nose\nimport numpy as np\nimport scipy.io\nfrom pkg_resources import resource_stream\n\nimport gammatone.filters\n\nREF_DATA_FILENAME = 'data/test_filterbank_data.mat'\n\nINPUT_KEY  = 'erb_filterbank_inputs'\nRESULT_KEY = 'erb_filterbank_results'\n\nINPUT_COLS  = ('fcoefs', 'wave')\nRESULT_COLS = ('filterbank',)\n\ndef load_reference_data():\n    \"\"\" Load test data generated from the reference code \"\"\"\n    # Load test data\n    with resource_stream(__name__, REF_DATA_FILENAME) as test_data:\n        data = scipy.io.loadmat(test_data, squeeze_me=False)\n    \n    zipped_data = zip(data[INPUT_KEY], data[RESULT_KEY])\n    \n    for inputs, refs in zipped_data:\n        input_dict = dict(zip(INPUT_COLS, map(np.squeeze, inputs)))\n        ref_dict = dict(zip(RESULT_COLS, map(np.squeeze, refs)))\n        yield (input_dict, ref_dict)\n\n\ndef test_ERB_filterbank_known_values():\n    for inputs, refs in load_reference_data():\n        args = (\n            inputs['wave'],\n            inputs['fcoefs'],\n        )\n        \n        expected = (refs['filterbank'],)\n        \n        yield ERBFilterBankTester(args, expected)\n\n\nclass ERBFilterBankTester:\n    \n    def __init__(self, args, expected):\n        self.signal = args[0]\n        self.fcoefs = args[1]\n        self.expected = expected[0]\n        \n        self.description = (\n            \"Gammatone filterbank result for {:.1f} ... {:.1f}\".format(\n                self.fcoefs[0][0],\n                self.fcoefs[0][1]\n        ))\n    \n    def __call__(self):\n        result = gammatone.filters.erb_filterbank(self.signal, self.fcoefs)\n        assert np.allclose(result, self.expected, rtol=1e-5, atol=1e-12)\n\n\nif __name__ == '__main__':\n    nose.main()\n"
  },
  {
    "path": "tests/test_gammatone_filters.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n# \n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nimport nose\nimport numpy as np\nimport scipy.io\nfrom pkg_resources import resource_stream\n\nimport gammatone.filters\n\nREF_DATA_FILENAME = 'data/test_erb_filter_data.mat'\n\nINPUT_KEY  = 'erb_filter_inputs'\nRESULT_KEY = 'erb_filter_results'\n\nINPUT_COLS  = ('fs', 'cfs')\nRESULT_COLS = ('fcoefs',)\n\ndef load_reference_data():\n    \"\"\" Load test data generated from the reference code \"\"\"\n    # Load test data\n    with resource_stream(__name__, REF_DATA_FILENAME) as test_data:\n        data = scipy.io.loadmat(test_data, squeeze_me=False)\n    \n    zipped_data = zip(data[INPUT_KEY], data[RESULT_KEY])\n    \n    for inputs, refs in zipped_data:\n        input_dict = dict(zip(INPUT_COLS, map(np.squeeze, inputs)))\n        ref_dict = dict(zip(RESULT_COLS, map(np.squeeze, refs)))\n        yield (input_dict, ref_dict)\n\n\ndef test_make_ERB_filters_known_values():\n    for inputs, refs in load_reference_data():\n        args = (\n            inputs['fs'],\n            inputs['cfs'],\n        )\n        \n        expected = (refs['fcoefs'],)\n        \n        yield MakeERBFiltersTester(args, expected)\n\n\nclass MakeERBFiltersTester:\n    \n    def __init__(self, args, expected):\n        self.fs = args[0]\n        self.cfs = args[1]\n        self.expected = expected[0]\n        self.description = (\n            \"Gammatone filters for {:f}, {:.1f} ... {:.1f}\".format(\n                float(self.fs),\n                float(self.cfs[0]),\n                float(self.cfs[-1])\n        ))\n    \n    def __call__(self):\n        result = gammatone.filters.make_erb_filters(self.fs, self.cfs)\n        assert np.allclose(result, self.expected, rtol=1e-6, atol=1e-12)\n\nif __name__ == '__main__':\n    nose.main()\n"
  },
  {
    "path": "tests/test_gammatonegram.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n#\n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfrom mock import patch\nimport nose\nimport numpy as np\nimport scipy.io\nfrom pkg_resources import resource_stream\n\nimport gammatone.gtgram\n\nREF_DATA_FILENAME = 'data/test_gammatonegram_data.mat'\n\nINPUT_KEY  = 'gammatonegram_inputs'\nMOCK_KEY   = 'gammatonegram_mocks'\nRESULT_KEY = 'gammatonegram_results'\n\nINPUT_COLS  = ('name', 'wave', 'fs', 'twin', 'thop', 'channels', 'fmin')\nMOCK_COLS   = ('erb_fb', 'erb_fb_cols')\nRESULT_COLS = ('gtgram', 'nwin', 'hopsamps', 'ncols')\n\n\ndef load_reference_data():\n    \"\"\" Load test data generated from the reference code \"\"\"\n    # Load test data\n    with resource_stream(__name__, REF_DATA_FILENAME) as test_data:\n        data = scipy.io.loadmat(test_data, squeeze_me=True)\n\n    zipped_data = zip(data[INPUT_KEY], data[MOCK_KEY], data[RESULT_KEY])\n    for inputs, mocks, refs in zipped_data:\n        input_dict = dict(zip(INPUT_COLS, inputs))\n        mock_dict  = dict(zip(MOCK_COLS, mocks))\n        ref_dict = dict(zip(RESULT_COLS, refs))\n        yield (input_dict, mock_dict, ref_dict)\n\n\ndef test_nstrides():\n    \"\"\" Test gamamtonegram stride calculations \"\"\"\n    for inputs, mocks, refs in load_reference_data():\n        args = (\n            inputs['fs'],\n            inputs['twin'],\n            inputs['thop'],\n            mocks['erb_fb_cols']\n        )\n\n        expected = (\n            refs['nwin'],\n            refs['hopsamps'],\n            refs['ncols']\n        )\n\n        yield GTGramStrideTester(inputs['name'], args, expected)\n\n\nclass GTGramStrideTester:\n    \"\"\" Testing class for gammatonegram stride calculation \"\"\"\n\n    def __init__(self, name, inputs, expected):\n        self.inputs      = inputs\n        self.expected    = expected\n        self.description = \"Gammatonegram strides for {:s}\".format(name)\n\n    def __call__(self):\n        results = gammatone.gtgram.gtgram_strides(*self.inputs)\n\n        diagnostic = (\n            \"result: {:s}, expected: {:s}\".format(\n                str(results),\n                str(self.expected)\n            )\n        )\n\n        # These are integer values, so use direct equality\n        assert results == self.expected\n\n\n# TODO: possibly mock out gtgram_strides\n\ndef test_gtgram():\n    for inputs, mocks, refs in load_reference_data():\n        args = (\n            inputs['fs'],\n            inputs['twin'],\n            inputs['thop'],\n            inputs['channels'],\n            inputs['fmin']\n        )\n\n        yield GammatonegramTester(\n            inputs['name'],\n            args,\n            inputs['wave'],\n            mocks['erb_fb'],\n            refs['gtgram']\n        )\n\nclass GammatonegramTester:\n    \"\"\" Testing class for gammatonegram calculation \"\"\"\n\n    def __init__(self, name, args, sig, erb_fb_out, expected):\n        self.signal = np.asarray(sig)\n        self.expected = np.asarray(expected)\n        self.erb_fb_out = np.asarray(erb_fb_out)\n        self.args = args\n\n        self.description = \"Gammatonegram for {:s}\".format(name)\n\n    def __call__(self):\n        with patch(\n            'gammatone.gtgram.erb_filterbank',\n            return_value=self.erb_fb_out):\n\n            result = gammatone.gtgram.gtgram(self.signal, *self.args)\n\n            max_diff = np.max(np.abs(result - self.expected))\n            diagnostic = \"Maximum difference: {:6e}\".format(max_diff)\n\n            assert np.allclose(result, self.expected, rtol=1e-6, atol=1e-12), diagnostic\n\nif __name__ == '__main__':\n    nose.main()\n"
  },
  {
    "path": "tests/test_specgram.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2014 Jason Heeris, jason.heeris@gmail.com\n#\n# This file is part of the gammatone toolkit, and is licensed under the 3-clause\n# BSD license: https://github.com/detly/gammatone/blob/master/COPYING\nfrom mock import patch\nimport nose\nimport numpy as np\nimport scipy.io\nfrom pkg_resources import resource_stream\n\nimport gammatone.fftweight\n\nREF_DATA_FILENAME = 'data/test_specgram_data.mat'\n\nINPUT_KEY  = 'specgram_inputs'\nMOCK_KEY   = 'specgram_mocks'\nRESULT_KEY = 'specgram_results'\n\nINPUT_COLS  = ('name', 'wave', 'nfft', 'fs', 'nwin', 'nhop')\nMOCK_COLS   = ('window',)\nRESULT_COLS = ('res',)\n\n\ndef load_reference_data():\n    \"\"\" Load test data generated from the reference code \"\"\"\n    # Load test data\n    with resource_stream(__name__, REF_DATA_FILENAME) as test_data:\n        data = scipy.io.loadmat(test_data, squeeze_me=False)\n\n    zipped_data = zip(data[INPUT_KEY], data[MOCK_KEY], data[RESULT_KEY])\n    for inputs, mocks, refs in zipped_data:\n        input_dict = dict(zip(INPUT_COLS, inputs))\n        mock_dict  = dict(zip(MOCK_COLS, mocks))\n        ref_dict = dict(zip(RESULT_COLS, refs))\n\n        yield (input_dict, mock_dict, ref_dict)\n\n\ndef test_specgram():\n    for inputs, mocks, refs in load_reference_data():\n        args = (\n            inputs['nfft'],\n            inputs['fs'],\n            inputs['nwin'],\n            inputs['nhop'],\n        )\n\n        yield SpecgramTester(\n            inputs['name'][0],\n            args,\n            inputs['wave'],\n            mocks['window'],\n            refs['res']\n        )\n\nclass SpecgramTester:\n    \"\"\" Testing class for specgram replacement calculation \"\"\"\n\n    def __init__(self, name, args, sig, window, expected):\n        self.signal = np.asarray(sig).squeeze()\n        self.expected = np.asarray(expected).squeeze()\n        self.args = [int(a.squeeze()) for a in args]\n        self.window = window.squeeze()\n        self.description = \"Specgram for {:s}\".format(name)\n\n\n    def __call__(self):\n        with patch(\n                'gammatone.fftweight.specgram_window',\n                return_value=self.window):\n            result = gammatone.fftweight.specgram(self.signal, *self.args)\n\n            max_diff = np.max(np.abs(result - self.expected))\n            diagnostic = \"Maximum difference: {:6e}\".format(max_diff)\n\n            assert np.allclose(result, self.expected, rtol=1e-6, atol=1e-12), diagnostic\n\nif __name__ == '__main__':\n    nose.main()\n"
  }
]