shithub: aubio

Download patch

ref: 568fc6056c256200f7ccec8e6e50230b0e8822fb
parent: a9e3bd03b1c01cc4974e7b0a7a9fa49c0a19caf4
parent: bc1ed6368f0057be27c5aea0267e94f79884acea
author: Paul Brossier <piem@piem.org>
date: Mon Nov 5 10:43:02 EST 2018

Merge branch 'master' into feature/pytest

--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,5 @@
 # test sounds
 python/tests/sounds
 aubio.egg-info
+.eggs
+.cache
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,9 +1,9 @@
 include AUTHORS COPYING README.md VERSION ChangeLog
 include python/README.md
 include this_version.py
+include waf
+recursive-include waflib *.py
 include Makefile wscript */wscript_build
-include waf waflib/* waflib/*/*
-exclude waflib/__pycache__/*
 include aubio.pc.in
 include requirements.txt
 include src/*.c src/*.h
--- a/doc/py_io.rst
+++ b/doc/py_io.rst
@@ -39,7 +39,7 @@
     If opened with more than one channel, the frames will be
     down-mixed to produce the new samples.
 
-    return: A tuple of one array of samples and one integer.
+    :returns: A tuple of one array of samples and one integer.
     :rtype: (array, int)
 
     .. seealso:: :meth:`__next__`
--- a/python/demos/demo_filter.py
+++ b/python/demos/demo_filter.py
@@ -4,6 +4,7 @@
 import os.path
 import aubio
 
+
 def apply_filter(path, target):
     # open input file, get its samplerate
     s = aubio.source(path)
@@ -27,19 +28,20 @@
         # count frames read
         total_frames += read
         # end of file reached
-        if read < s.hop_size: break
+        if read < s.hop_size:
+            break
 
     # print some info
     duration = total_frames / float(samplerate)
     input_str = "input: {:s} ({:.2f} s, {:d} Hz)"
     output_str = "output: {:s}, A-weighting filtered ({:d} frames total)"
-    print (input_str.format(s.uri, duration, samplerate))
-    print (output_str.format(o.uri, total_frames))
+    print(input_str.format(s.uri, duration, samplerate))
+    print(output_str.format(o.uri, total_frames))
 
 if __name__ == '__main__':
     usage = "{:s} <input_file> [output_file]".format(sys.argv[0])
     if not 1 < len(sys.argv) < 4:
-        print (usage)
+        print(usage)
         sys.exit(1)
     if len(sys.argv) < 3:
         input_path = sys.argv[1]
--- a/python/demos/demo_filterbank.py
+++ b/python/demos/demo_filterbank.py
@@ -1,30 +1,44 @@
 #! /usr/bin/env python
 
-from aubio import filterbank, fvec
-from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title
-from numpy import vstack, arange
+"""Create a filterbank from a list of frequencies.
 
-win_s = 2048
+This demo uses `aubio.filterbank.set_triangle_bands` to build a set of
+triangular filters from a list of frequencies.
+
+The filterbank coefficients are then modified before being displayed."""
+
+import aubio
+import numpy as np
+import matplotlib.pyplot as plt
+
+# sampling rate and size of the fft
 samplerate = 48000
+win_s = 2048
 
+# define a list of custom frequency
 freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]
+# number of filters to create
 n_filters = len(freq_list) - 2
 
-f = filterbank(n_filters, win_s)
-freqs = fvec(freq_list)
+# create a new filterbank
+f = aubio.filterbank(n_filters, win_s)
+freqs = aubio.fvec(freq_list)
 f.set_triangle_bands(freqs, samplerate)
 
+# get the coefficients from the filterbank
 coeffs = f.get_coeffs()
-coeffs[4] *= 5.
-
+# apply a gain to fifth band
+coeffs[4] *= 6.
+# load the modified coeffs into the filterbank
 f.set_coeffs(coeffs)
 
-times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
-title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.')
-loglog(times.T, f.get_coeffs().T, '.-')
-xlim([50, samplerate/2])
-ylim([1.0e-6, 2.0e-2])
-xlabel('log frequency (Hz)')
-ylabel('log amplitude')
-
-show()
+# display the band gains in a loglog plot
+freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
+plt.title('filterbank built from a list of frequencies\n'
+          'The 5th band has been amplified by a factor 6.')
+plt.loglog(freqs.T, f.get_coeffs().T, '.-')
+plt.xlim([50, samplerate/2])
+plt.ylim([1.0e-6, 2.0e-2])
+plt.xlabel('log frequency (Hz)')
+plt.ylabel('log amplitude')
+plt.show()
--- a/python/demos/demo_source_simple.py
+++ b/python/demos/demo_source_simple.py
@@ -1,16 +1,20 @@
 #! /usr/bin/env python
+
+"""A simple example using aubio.source."""
+
 import sys
 import aubio
 
-samplerate = 0 # use original source samplerate
-hop_size = 256 # number of frames to read in one block
+samplerate = 0  # use original source samplerate
+hop_size = 256  # number of frames to read in one block
 src = aubio.source(sys.argv[1], samplerate, hop_size)
 total_frames = 0
 
 while True:
-    samples, read = src()     # read hop_size new samples from source
-    total_frames += read      # increment total number of frames
-    if read < hop_size: break # end of file reached
+    samples, read = src()  # read hop_size new samples from source
+    total_frames += read   # increment total number of frames
+    if read < hop_size:    # end of file reached
+        break
 
 fmt_string = "read {:d} frames at {:d}Hz from {:s}"
-print (fmt_string.format(total_frames, src.samplerate, src.uri))
+print(fmt_string.format(total_frames, src.samplerate, src.uri))
--- a/python/ext/py-sink.c
+++ b/python/ext/py-sink.c
@@ -14,7 +14,7 @@
 static char Py_sink_doc[] = ""
 "sink(path, samplerate=44100, channels=1)\n"
 "\n"
-"Open `path` to write a WAV file.\n"
+"Write audio samples to file.\n"
 "\n"
 "Parameters\n"
 "----------\n"
--- a/python/ext/py-source.c
+++ b/python/ext/py-source.c
@@ -18,7 +18,7 @@
 static char Py_source_doc[] = ""
 "source(path, samplerate=0, hop_size=512, channels=0)\n"
 "\n"
-"Create a new source, opening the given pathname for reading.\n"
+"Read audio samples from a media file.\n"
 "\n"
 "`source` open the file specified in `path` and creates a callable\n"
 "returning `hop_size` new audio samples at each invocation.\n"
@@ -247,7 +247,7 @@
 "\n"
 "Returns\n"
 "-------\n"
-"samples : numpy.ndarray, shape `(hop_size,)`, dtype aubio.float_type\n"
+"samples : numpy.ndarray\n"
 "    `fvec` of size `hop_size` containing the new samples.\n"
 "read : int\n"
 "    Number of samples read from the source, equals to `hop_size`\n"
@@ -283,7 +283,7 @@
 "\n"
 "Returns\n"
 "-------\n"
-"samples : np.ndarray([hop_size, channels], dtype=aubio.float_type)\n"
+"samples : numpy.ndarray\n"
 "    NumPy array of shape `(hop_size, channels)` containing the new\n"
 "    audio samples.\n"
 "read : int\n"
--- a/python/lib/aubio/__init__.py
+++ b/python/lib/aubio/__init__.py
@@ -29,6 +29,7 @@
 from .midiconv import *
 from .slicing import *
 
+
 class fvec(numpy.ndarray):
     """fvec(input_arg=1024)
     A vector holding float samples.
--- a/python/lib/aubio/cmd.py
+++ b/python/lib/aubio/cmd.py
@@ -11,6 +11,7 @@
 
 import sys
 import argparse
+import warnings
 import aubio
 
 def aubio_parser():
@@ -168,10 +169,10 @@
                 help="samplerate at which the file should be represented")
 
     def add_verbose_help(self):
-        self.add_argument("-v","--verbose",
+        self.add_argument("-v", "--verbose",
                 action="count", dest="verbose", default=1,
                 help="make lots of noise [default]")
-        self.add_argument("-q","--quiet",
+        self.add_argument("-q", "--quiet",
                 action="store_const", dest="verbose", const=0,
                 help="be quiet")
 
@@ -180,25 +181,25 @@
         self.add_hop_size(hop_size=hop_size)
 
     def add_buf_size(self, buf_size=512):
-        self.add_argument("-B","--bufsize",
+        self.add_argument("-B", "--bufsize",
                 action="store", dest="buf_size", default=buf_size,
                 metavar = "<size>", type=int,
                 help="buffer size [default=%d]" % buf_size)
 
     def add_hop_size(self, hop_size=256):
-        self.add_argument("-H","--hopsize",
+        self.add_argument("-H", "--hopsize",
                 metavar = "<size>", type=int,
                 action="store", dest="hop_size", default=hop_size,
                 help="overlap size [default=%d]" % hop_size)
 
     def add_method(self, method='default', helpstr='method'):
-        self.add_argument("-m","--method",
+        self.add_argument("-m", "--method",
                 metavar = "<method>", type=str,
                 action="store", dest="method", default=method,
                 help="%s [default=%s]" % (helpstr, method))
 
     def add_threshold(self, default=None):
-        self.add_argument("-t","--threshold",
+        self.add_argument("-t", "--threshold",
                 metavar = "<threshold>", type=float,
                 action="store", dest="threshold", default=default,
                 help="threshold [default=%s]" % default)
@@ -239,14 +240,16 @@
                  help=helpstr)
 
     def add_slicer_options(self):
-        self.add_argument("-o","--output", type = str,
+        self.add_argument("-o", "--output", type = str,
                 metavar = "<outputdir>",
                 action="store", dest="output_directory", default=None,
-                help="specify path where slices of the original file should be created")
+                help="specify path where slices of the original file should"
+                " be created")
         self.add_argument("--cut-until-nsamples", type = int,
                 metavar = "<samples>",
                 action = "store", dest = "cut_until_nsamples", default = None,
-                help="how many extra samples should be added at the end of each slice")
+                help="how many extra samples should be added at the end of"
+                " each slice")
         self.add_argument("--cut-every-nslices", type = int,
                 metavar = "<samples>",
                 action = "store", dest = "cut_every_nslices", default = None,
@@ -254,7 +257,8 @@
         self.add_argument("--cut-until-nslices", type = int,
                 metavar = "<slices>",
                 action = "store", dest = "cut_until_nslices", default = None,
-                help="how many extra slices should be added at the end of each slice")
+                help="how many extra slices should be added at the end of"
+                " each slice")
         self.add_argument("--create-first",
                 action = "store_true", dest = "create_first", default = False,
                 help="always include first slice")
@@ -288,7 +292,8 @@
             self.time2string = timefunc(args.time_format)
         if args.verbose > 2 and hasattr(self, 'options'):
             name = type(self).__name__.split('_')[1]
-            optstr = ' '.join(['running', name, 'with options', repr(self.options), '\n'])
+            optstr = ' '.join(['running', name, 'with options',
+                repr(self.options), '\n'])
             sys.stderr.write(optstr)
     def flush(self, frames_read, samplerate):
         # optionally called at the end of process
@@ -296,7 +301,7 @@
 
     def parse_options(self, args, valid_opts):
         # get any valid options found in a dictionnary of arguments
-        options = {k :v for k,v in vars(args).items() if k in valid_opts}
+        options = {k: v for k, v in vars(args).items() if k in valid_opts}
         self.options = options
 
     def remap_pvoc_options(self, options):
@@ -377,7 +382,7 @@
         if len(self.beat_locations) < 2:
             outstr = "unknown bpm"
         else:
-            bpms = 60./ np.diff(self.beat_locations)
+            bpms = 60. / np.diff(self.beat_locations)
             median_bpm = np.mean(bpms)
             if len(self.beat_locations) < 10:
                 outstr = "%.2f bpm (uncertain)" % median_bpm
@@ -398,14 +403,14 @@
     def __call__(self, block):
         return self.notes(block)
     def repr_res(self, res, frames_read, samplerate):
-        if res[2] != 0: # note off
+        if res[2] != 0:  # note off
             fmt_out = self.time2string(frames_read, samplerate)
             sys.stdout.write(fmt_out + '\n')
-        if res[0] != 0: # note on
+        if res[0] != 0:  # note on
             lastmidi = res[0]
             fmt_out = "%f\t" % lastmidi
             fmt_out += self.time2string(frames_read, samplerate)
-            sys.stdout.write(fmt_out) # + '\t')
+            sys.stdout.write(fmt_out)  # + '\t')
     def flush(self, frames_read, samplerate):
         eof = self.time2string(frames_read, samplerate)
         sys.stdout.write(eof + '\n')
@@ -472,13 +477,13 @@
         if aubio.silence_detection(block, self.silence) == 1:
             if self.wassilence != 1:
                 self.wassilence = 1
-                return 2 # newly found silence
-            return 1 # silence again
+                return 2   # newly found silence
+            return 1       # silence again
         else:
             if self.wassilence != 0:
                 self.wassilence = 0
-                return -1 # newly found noise
-            return 0 # noise again
+                return -1  # newly found noise
+            return 0       # noise again
 
     def repr_res(self, res, frames_read, samplerate):
         fmt_out = None
@@ -498,20 +503,47 @@
 
     def __call__(self, block):
         ret = super(process_cut, self).__call__(block)
-        if ret: self.slices.append(self.onset.get_last())
+        if ret:
+            self.slices.append(self.onset.get_last())
         return ret
 
     def flush(self, frames_read, samplerate):
-        from aubio.cut import _cut_slice
         _cut_slice(self.options, self.slices)
-        duration = float (frames_read) / float(samplerate)
-        base_info = '%(source_file)s' % {'source_file': self.options.source_uri}
+        duration = float(frames_read) / float(samplerate)
+        base_info = '%(source_file)s' % \
+                    {'source_file': self.options.source_uri}
         base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
-                {'duration': duration, 'samplerate': samplerate}
+                     {'duration': duration, 'samplerate': samplerate}
         info = "created %d slices from " % len(self.slices)
         info += base_info
         sys.stderr.write(info)
 
+def _cut_slice(options, timestamps):
+    # cutting pass
+    nstamps = len(timestamps)
+    if nstamps > 0:
+        # generate output files
+        timestamps_end = None
+        if options.cut_every_nslices:
+            timestamps = timestamps[::options.cut_every_nslices]
+            nstamps = len(timestamps)
+        if options.cut_until_nslices and options.cut_until_nsamples:
+            msg = "using cut_until_nslices, but cut_until_nsamples is set"
+            warnings.warn(msg)
+        if options.cut_until_nsamples:
+            lag = options.cut_until_nsamples
+            timestamps_end = [t + lag for t in timestamps[1:]]
+            timestamps_end += [1e120]
+        if options.cut_until_nslices:
+            slice_lag = options.cut_until_nslices
+            timestamps_end = [t for t in timestamps[1 + slice_lag:]]
+            timestamps_end += [1e120] * (options.cut_until_nslices + 1)
+        aubio.slice_source_at_stamps(options.source_uri,
+                timestamps, timestamps_end = timestamps_end,
+                output_dir = options.output_directory,
+                samplerate = options.samplerate,
+                create_first = options.create_first)
+
 def main():
     parser = aubio_parser()
     if sys.version_info[0] != 3:
@@ -525,12 +557,12 @@
         parser_root.add_argument('-V', '--version', help="show version",
                 action="store_true", dest="show_version")
         args, extras = parser_root.parse_known_args()
-        if args.show_version == False: # no -V, forward to parser
+        if not args.show_version:  # no -V, forward to parser
             args = parser.parse_args(extras, namespace=args)
-        elif len(extras) != 0: # -V with other arguments, print help
+        elif len(extras) != 0:     # -V with other arguments, print help
             parser.print_help()
             sys.exit(1)
-    else: # in py3, we can simply use parser directly
+    else:  # in py3, we can simply use parser directly
         args = parser.parse_args()
     if 'show_version' in args and args.show_version:
         sys.stdout.write('aubio version ' + aubio.version + '\n')
@@ -537,7 +569,8 @@
         sys.exit(0)
     elif 'verbose' in args and args.verbose > 3:
         sys.stderr.write('aubio version ' + aubio.version + '\n')
-    if 'command' not in args or args.command is None or args.command in ['help']:
+    if 'command' not in args or args.command is None \
+            or args.command in ['help']:
         # no command given, print help and return 1
         parser.print_help()
         if args.command and args.command in ['help']:
@@ -571,7 +604,8 @@
                 # increment total number of frames read
                 frames_read += read
                 # exit loop at end of file
-                if read < a_source.hop_size: break
+                if read < a_source.hop_size:
+                    break
             # flush the processor if needed
             processor.flush(frames_read, a_source.samplerate)
             if args.verbose > 1:
@@ -579,7 +613,7 @@
                 fmt_string += " ({:d} samples in {:d} blocks of {:d})"
                 fmt_string += " from {:s} at {:d}Hz\n"
                 sys.stderr.write(fmt_string.format(
-                        frames_read/float(a_source.samplerate),
+                        frames_read / float(a_source.samplerate),
                         frames_read,
                         frames_read // a_source.hop_size + 1,
                         a_source.hop_size,
--- a/python/lib/aubio/cut.py
+++ b/python/lib/aubio/cut.py
@@ -5,25 +5,25 @@
 """
 
 import sys
-from aubio.cmd import AubioArgumentParser
+from aubio.cmd import AubioArgumentParser, _cut_slice
 
 def aubio_cut_parser():
     parser = AubioArgumentParser()
     parser.add_input()
-    parser.add_argument("-O","--onset-method",
+    parser.add_argument("-O", "--onset-method",
             action="store", dest="onset_method", default='default',
             metavar = "<onset_method>",
             help="onset detection method [default=default] \
                     complexdomain|hfc|phase|specdiff|energy|kl|mkl")
     # cutting methods
-    parser.add_argument("-b","--beat",
+    parser.add_argument("-b", "--beat",
             action="store_true", dest="beat", default=False,
             help="slice at beat locations")
     """
-    parser.add_argument("-S","--silencecut",
+    parser.add_argument("-S", "--silencecut",
             action="store_true", dest="silencecut", default=False,
             help="use silence locations")
-    parser.add_argument("-s","--silence",
+    parser.add_argument("-s", "--silence",
             metavar = "<value>",
             action="store", dest="silence", default=-70,
             help="silence threshold [default=-70]")
@@ -30,58 +30,58 @@
             """
     # algorithm parameters
     parser.add_buf_hop_size()
-    parser.add_argument("-t","--threshold", "--onset-threshold",
+    parser.add_argument("-t", "--threshold", "--onset-threshold",
             metavar = "<threshold>", type=float,
             action="store", dest="threshold", default=0.3,
             help="onset peak picking threshold [default=0.3]")
-    parser.add_argument("-c","--cut",
+    parser.add_argument("-c", "--cut",
             action="store_true", dest="cut", default=False,
             help="cut input sound file at detected labels")
     parser.add_minioi()
 
     """
-    parser.add_argument("-D","--delay",
+    parser.add_argument("-D", "--delay",
             action = "store", dest = "delay", type = float,
             metavar = "<seconds>", default=0,
             help="number of seconds to take back [default=system]\
                     default system delay is 3*hopsize/samplerate")
-    parser.add_argument("-C","--dcthreshold",
+    parser.add_argument("-C", "--dcthreshold",
             metavar = "<value>",
             action="store", dest="dcthreshold", default=1.,
             help="onset peak picking DC component [default=1.]")
-    parser.add_argument("-L","--localmin",
+    parser.add_argument("-L", "--localmin",
             action="store_true", dest="localmin", default=False,
             help="use local minima after peak detection")
-    parser.add_argument("-d","--derivate",
+    parser.add_argument("-d", "--derivate",
             action="store_true", dest="derivate", default=False,
             help="derivate onset detection function")
-    parser.add_argument("-z","--zerocross",
+    parser.add_argument("-z", "--zerocross",
             metavar = "<value>",
             action="store", dest="zerothres", default=0.008,
             help="zero-crossing threshold for slicing [default=0.00008]")
     # plotting functions
-    parser.add_argument("-p","--plot",
+    parser.add_argument("-p", "--plot",
             action="store_true", dest="plot", default=False,
             help="draw plot")
-    parser.add_argument("-x","--xsize",
+    parser.add_argument("-x", "--xsize",
             metavar = "<size>",
             action="store", dest="xsize", default=1.,
             type=float, help="define xsize for plot")
-    parser.add_argument("-y","--ysize",
+    parser.add_argument("-y", "--ysize",
             metavar = "<size>",
             action="store", dest="ysize", default=1.,
             type=float, help="define ysize for plot")
-    parser.add_argument("-f","--function",
+    parser.add_argument("-f", "--function",
             action="store_true", dest="func", default=False,
             help="print detection function")
-    parser.add_argument("-n","--no-onsets",
+    parser.add_argument("-n", "--no-onsets",
             action="store_true", dest="nplot", default=False,
             help="do not plot detected onsets")
-    parser.add_argument("-O","--outplot",
+    parser.add_argument("-O", "--outplot",
             metavar = "<output_image>",
             action="store", dest="outplot", default=None,
             help="save plot to output.{ps,png}")
-    parser.add_argument("-F","--spectrogram",
+    parser.add_argument("-F", "--spectrogram",
             action="store_true", dest="spectro", default=False,
             help="add spectrogram to the plot")
     """
@@ -105,9 +105,11 @@
         options.samplerate = samplerate
 
     if options.beat:
-        o = tempo(options.onset_method, bufsize, hopsize, samplerate=samplerate)
+        o = tempo(options.onset_method, bufsize, hopsize,
+                samplerate=samplerate)
     else:
-        o = onset(options.onset_method, bufsize, hopsize, samplerate=samplerate)
+        o = onset(options.onset_method, bufsize, hopsize,
+                samplerate=samplerate)
         if options.minioi:
             if options.minioi.endswith('ms'):
                 o.set_minioi_ms(int(options.minioi[:-2]))
@@ -122,37 +124,15 @@
     while True:
         samples, read = s()
         if o(samples):
-            timestamps.append (o.get_last())
-            if options.verbose: print ("%.4f" % o.get_last_s())
+            timestamps.append(o.get_last())
+            if options.verbose:
+                print("%.4f" % o.get_last_s())
         total_frames += read
-        if read < hopsize: break
+        if read < hopsize:
+            break
     del s
     return timestamps, total_frames
 
-def _cut_slice(options, timestamps):
-    # cutting pass
-    nstamps = len(timestamps)
-    if nstamps > 0:
-        # generate output files
-        from aubio.slicing import slice_source_at_stamps
-        timestamps_end = None
-        if options.cut_every_nslices:
-            timestamps = timestamps[::options.cut_every_nslices]
-            nstamps = len(timestamps)
-        if options.cut_until_nslices and options.cut_until_nsamples:
-            print ("warning: using cut_until_nslices, but cut_until_nsamples is set")
-        if options.cut_until_nsamples:
-            timestamps_end = [t + options.cut_until_nsamples for t in timestamps[1:]]
-            timestamps_end += [ 1e120 ]
-        if options.cut_until_nslices:
-            timestamps_end = [t for t in timestamps[1 + options.cut_until_nslices:]]
-            timestamps_end += [ 1e120 ] * (options.cut_until_nslices + 1)
-        slice_source_at_stamps(options.source_uri,
-                timestamps, timestamps_end = timestamps_end,
-                output_dir = options.output_directory,
-                samplerate = options.samplerate,
-                create_first = options.create_first)
-
 def main():
     parser = aubio_cut_parser()
     options = parser.parse_args()
@@ -167,7 +147,7 @@
     timestamps, total_frames = _cut_analyze(options)
 
     # print some info
-    duration = float (total_frames) / float(options.samplerate)
+    duration = float(total_frames) / float(options.samplerate)
     base_info = '%(source_uri)s' % {'source_uri': options.source_uri}
     base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
             {'duration': duration, 'samplerate': options.samplerate}
--- a/python/lib/aubio/midiconv.py
+++ b/python/lib/aubio/midiconv.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
 """ utilities to convert midi note number to and from note names """
 
-__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
-
 import sys
 from ._aubio import freqtomidi, miditofreq
 
+__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
+
 py3 = sys.version_info[0] == 3
 if py3:
     str_instances = str
@@ -14,6 +14,7 @@
     str_instances = (str, unicode)
     int_instances = (int, long)
 
+
 def note2midi(note):
     """Convert note name to midi note number.
 
@@ -55,13 +56,14 @@
     --------
     midi2note, freqtomidi, miditofreq
     """
-    _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
+    _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7,
+                        'A': 9, 'B': 11}
     _valid_modifiers = {
-            u'𝄫': -2,                        # double flat
-            u'♭': -1, 'b': -1, '\u266d': -1, # simple flat
-            u'♮': 0, '\u266e': 0, None: 0,   # natural
-            '#': +1, u'♯': +1, '\u266f': +1, # sharp
-            u'𝄪': +2,                        # double sharp
+            u'𝄫': -2,                         # double flat
+            u'♭': -1, 'b': -1, '\u266d': -1,  # simple flat
+            u'♮': 0, '\u266e': 0, None: 0,    # natural
+            '#': +1, u'♯': +1, '\u266f': +1,  # sharp
+            u'𝄪': +2,                         # double sharp
             }
     _valid_octaves = range(-1, 10)
     if not isinstance(note, str_instances):
@@ -70,7 +72,7 @@
     if len(note) not in range(2, 5):
         msg = "string of 2 to 4 characters expected, got {:d} ({:s})"
         raise ValueError(msg.format(len(note), note))
-    notename, modifier, octave = [None]*3
+    notename, modifier, octave = [None] * 3
 
     if len(note) == 4:
         notename, modifier, octave_sign, octave = note
@@ -93,12 +95,13 @@
     if octave not in _valid_octaves:
         raise ValueError("%s is not a valid octave" % octave)
 
-    midi = 12 + octave * 12 + _valid_notenames[notename] \
-            + _valid_modifiers[modifier]
+    midi = (octave + 1) * 12 + _valid_notenames[notename] \
+                             + _valid_modifiers[modifier]
     if midi > 127:
         raise ValueError("%s is outside of the range C-2 to G8" % note)
     return midi
 
+
 def midi2note(midi):
     """Convert midi note number to note name.
 
@@ -136,9 +139,10 @@
         msg = "an integer between 0 and 127 is excepted, got {:d}"
         raise ValueError(msg.format(midi))
     _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#',
-            'A', 'A#', 'B']
+                        'A', 'A#', 'B']
     return _valid_notenames[midi % 12] + str(int(midi / 12) - 1)
 
+
 def freq2note(freq):
     """Convert frequency in Hz to nearest note name.
 
@@ -161,6 +165,7 @@
     """
     nearest_note = int(freqtomidi(freq) + .5)
     return midi2note(nearest_note)
+
 
 def note2freq(note):
     """Convert note name to corresponding frequency, in Hz.
--- a/python/lib/aubio/slicing.py
+++ b/python/lib/aubio/slicing.py
@@ -5,6 +5,7 @@
 
 _max_timestamp = 1e120
 
+
 def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,
                            output_dir=None, samplerate=0, hopsize=256,
                            create_first=False):
@@ -72,7 +73,7 @@
     the file.
     """
 
-    if timestamps is None or len(timestamps) == 0:
+    if not timestamps:
         raise ValueError("no timestamps given")
 
     if timestamps[0] != 0 and create_first:
@@ -89,7 +90,6 @@
         timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]
 
     regions = list(zip(timestamps, timestamps_end))
-    #print regions
 
     source_base_name, _ = os.path.splitext(os.path.basename(source_file))
     if output_dir is not None:
@@ -97,8 +97,8 @@
             os.makedirs(output_dir)
         source_base_name = os.path.join(output_dir, source_base_name)
 
-    def new_sink_name(source_base_name, timestamp, samplerate):
-        """ create a sink based on a timestamp in samples, converted in seconds """
+    def _new_sink_name(source_base_name, timestamp, samplerate):
+        # create name based on a timestamp in samples, converted in seconds
         timestamp_seconds = timestamp / float(samplerate)
         return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'
 
@@ -113,16 +113,17 @@
         # get hopsize new samples from source
         vec, read = _source.do_multi()
         # if the total number of frames read will exceed the next region start
-        while len(regions) and total_frames + read >= regions[0][0]:
-            #print "getting", regions[0], "at", total_frames
+        while regions and total_frames + read >= regions[0][0]:
             # get next region
             start_stamp, end_stamp = regions.pop(0)
             # create a name for the sink
-            new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate)
+            new_sink_path = _new_sink_name(source_base_name, start_stamp,
+                                           samplerate)
             # create its sink
             _sink = sink(new_sink_path, samplerate, _source.channels)
             # create a dictionary containing all this
-            new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink}
+            new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp,
+                         'sink': _sink}
             # append the dictionary to the current list of slices
             slices.append(new_slice)
 
@@ -134,13 +135,11 @@
             start = max(start_stamp - total_frames, 0)
             # number of samples yet to written be until end of region
             remaining = end_stamp - total_frames + 1
-            #print current_slice, remaining, start
             # not enough frames remaining, time to split
             if remaining < read:
                 if remaining > start:
                     # write remaining samples from current region
                     _sink.do_multi(vec[:, start:remaining], remaining - start)
-                    #print("closing region", "remaining", remaining)
                     # close this file
                     _sink.close()
             elif read > start:
@@ -149,6 +148,6 @@
         total_frames += read
         # remove old slices
         slices = list(filter(lambda s: s['end_stamp'] > total_frames,
-            slices))
+                             slices))
         if read < hopsize:
             break
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,12 @@
 #! /usr/bin/env python
 
-import sys, os.path, glob
+import sys
+import os.path
+import glob
 from setuptools import setup, Extension
 
 # add ./python/lib to current path
-sys.path.append(os.path.join('python', 'lib'))
+sys.path.append(os.path.join('python', 'lib'))  # noqa
 from moresetuptools import build_ext, CleanGenerated
 
 # function to generate gen/*.{c,h}
@@ -18,15 +20,16 @@
 define_macros = [('AUBIO_VERSION', '%s' % __aubio_version__)]
 extra_link_args = []
 
-include_dirs += [ 'python/ext' ]
+include_dirs += ['python/ext']
 try:
     import numpy
-    include_dirs += [ numpy.get_include() ]
+    include_dirs += [numpy.get_include()]
 except ImportError:
     pass
 
 if sys.platform.startswith('darwin'):
-    extra_link_args += ['-framework','CoreFoundation', '-framework','AudioToolbox']
+    extra_link_args += ['-framework', 'CoreFoundation',
+            '-framework', 'AudioToolbox']
 
 sources = sorted(glob.glob(os.path.join('python', 'ext', '*.c')))
 
@@ -37,10 +40,11 @@
     extra_link_args = extra_link_args,
     define_macros = define_macros)
 
-if os.path.isfile('src/aubio.h'):
-    if not os.path.isdir(os.path.join('build','src')):
-        pass
-        #__version__ += 'a2' # python only version
+# TODO: find a way to track if package is built against libaubio
+# if os.path.isfile('src/aubio.h'):
+#     if not os.path.isdir(os.path.join('build','src')):
+#         pass
+#         #__version__ += 'a2' # python only version
 
 classifiers = [
     'Development Status :: 4 - Beta',
@@ -54,13 +58,14 @@
     'Operating System :: Microsoft :: Windows',
     'Programming Language :: C',
     'Programming Language :: Python',
-    'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
+    'License :: OSI Approved :: '
+    'GNU General Public License v3 or later (GPLv3+)',
     ]
 
 distrib = setup(name='aubio',
     version = __version__,
     packages = ['aubio'],
-    package_dir = {'aubio':'python/lib/aubio'},
+    package_dir = {'aubio': 'python/lib/aubio'},
     ext_modules = [aubio_extension],
     description = 'a collection of tools for music analysis',
     long_description = 'a collection of tools for music analysis',
--- a/wscript
+++ b/wscript
@@ -42,8 +42,9 @@
             default = "release",
             choices = ('debug', 'release'),
             dest = 'build_type',
-            help = 'whether to compile with (--build-type=release) or without (--build-type=debug) '\
-              ' compiler opimizations [default: release]')
+            help = 'whether to compile with (--build-type=release)' \
+                    ' or without (--build-type=debug)' \
+                    ' compiler opimizations [default: release]')
     add_option_enable_disable(ctx, 'fftw3f', default = False,
             help_str = 'compile with fftw3f instead of ooura (recommended)',
             help_disable_str = 'do not compile with fftw3f')
@@ -109,7 +110,8 @@
             help_disable_str = 'do not build examples')
 
     ctx.add_option('--with-target-platform', type='string',
-            help='set target platform for cross-compilation', dest='target_platform')
+            help='set target platform for cross-compilation',
+            dest='target_platform')
 
     ctx.load('compiler_c')
     ctx.load('waf_unit_test')
@@ -205,13 +207,15 @@
             ctx.define('HAVE_SINK_APPLE_AUDIO', 1)
             ctx.msg('Checking for AudioToolbox.framework', 'yes')
         else:
-            ctx.msg('Checking for AudioToolbox.framework', 'no (disabled)', color = 'YELLOW')
+            ctx.msg('Checking for AudioToolbox.framework', 'no (disabled)',
+                    color = 'YELLOW')
         if (ctx.options.enable_accelerate != False):
             ctx.define('HAVE_ACCELERATE', 1)
             ctx.env.FRAMEWORK += ['Accelerate']
             ctx.msg('Checking for Accelerate framework', 'yes')
         else:
-            ctx.msg('Checking for Accelerate framework', 'no (disabled)', color = 'YELLOW')
+            ctx.msg('Checking for Accelerate framework', 'no (disabled)',
+                    color = 'YELLOW')
 
     if target_platform in [ 'ios', 'iosimulator' ]:
         MINSDKVER="6.1"
@@ -265,9 +269,11 @@
 
         # tell emscripten functions we want to expose
         from python.lib.gen_external import get_c_declarations, \
-                get_cpp_objects_from_c_declarations, get_all_func_names_from_lib, \
+                get_cpp_objects_from_c_declarations, \
+                get_all_func_names_from_lib, \
                 generate_lib_from_c_declarations
-        c_decls = get_c_declarations(usedouble=False)  # emscripten can't use double
+        # emscripten can't use double
+        c_decls = get_c_declarations(usedouble=False)
         objects = list(get_cpp_objects_from_c_declarations(c_decls))
         # ensure that aubio structs are exported
         objects += ['fvec_t', 'cvec_t', 'fmat_t']
@@ -274,7 +280,8 @@
         lib = generate_lib_from_c_declarations(objects, c_decls)
         exported_funcnames = get_all_func_names_from_lib(lib)
         c_mangled_names = ['_' + s for s in exported_funcnames]
-        ctx.env.LINKFLAGS_cshlib += ['-s', 'EXPORTED_FUNCTIONS=%s' % c_mangled_names]
+        ctx.env.LINKFLAGS_cshlib += ['-s',
+                'EXPORTED_FUNCTIONS=%s' % c_mangled_names]
 
     # check support for C99 __VA_ARGS__ macros
     check_c99_varargs = '''
@@ -304,8 +311,8 @@
 
     # check for Intel IPP
     if (ctx.options.enable_intelipp != False):
-        has_ipp_headers = ctx.check(header_name=['ippcore.h', 'ippvm.h', 'ipps.h'],
-                mandatory = False)
+        has_ipp_headers = ctx.check(header_name=['ippcore.h', 'ippvm.h',
+            'ipps.h'], mandatory = False)
         has_ipp_libs = ctx.check(lib=['ippcore', 'ippvm', 'ipps'],
                 uselib_store='INTEL_IPP', mandatory = False)
         if (has_ipp_headers and has_ipp_libs):
@@ -312,7 +319,8 @@
             ctx.msg('Checking if Intel IPP is available', 'yes')
             ctx.define('HAVE_INTEL_IPP', 1)
             if ctx.env.CC_NAME == 'msvc':
-                # force linking multi-threaded static IPP libraries on Windows with msvc
+                # force linking multi-threaded static IPP libraries on Windows
+                # with msvc
                 ctx.define('_IPP_SEQUENTIAL_STATIC', 1)
         else:
             ctx.msg('Checking if Intel IPP is available', 'no')
@@ -361,11 +369,12 @@
     # check for libsamplerate
     if (ctx.options.enable_double):
         if (ctx.options.enable_samplerate):
-            ctx.fatal("Could not compile aubio in double precision mode with libsamplerate")
+            ctx.fatal("Could not compile aubio in double precision mode' \
+                    ' with libsamplerate")
         else:
             ctx.options.enable_samplerate = False
-            ctx.msg('Checking if using samplerate', 'no (disabled in double precision mode)',
-                    color = 'YELLOW')
+            ctx.msg('Checking if using samplerate',
+                    'no (disabled in double precision mode)', color = 'YELLOW')
     if (ctx.options.enable_samplerate != False):
         ctx.check_cfg(package = 'samplerate',
                 args = '--cflags --libs samplerate >= 0.0.15',
@@ -408,7 +417,8 @@
             ctx.msg(msg_check, 'not found (missing avformat)', color = 'YELLOW')
         elif 'HAVE_AVUTIL' not in ctx.env:
             ctx.msg(msg_check, 'not found (missing avutil)', color = 'YELLOW')
-        elif 'HAVE_SWRESAMPLE' not in ctx.env and 'HAVE_AVRESAMPLE' not in ctx.env:
+        elif 'HAVE_SWRESAMPLE' not in ctx.env \
+                and 'HAVE_AVRESAMPLE' not in ctx.env:
             resample_missing = 'not found (avresample or swresample required)'
             ctx.msg(msg_check, resample_missing, color = 'YELLOW')
         else:
@@ -421,10 +431,12 @@
 
     if (ctx.options.enable_wavread != False):
         ctx.define('HAVE_WAVREAD', 1)
-    ctx.msg('Checking if using source_wavread', ctx.options.enable_wavread and 'yes' or 'no')
+    ctx.msg('Checking if using source_wavread',
+            ctx.options.enable_wavread and 'yes' or 'no')
     if (ctx.options.enable_wavwrite!= False):
         ctx.define('HAVE_WAVWRITE', 1)
-    ctx.msg('Checking if using sink_wavwrite', ctx.options.enable_wavwrite and 'yes' or 'no')
+    ctx.msg('Checking if using sink_wavwrite',
+            ctx.options.enable_wavwrite and 'yes' or 'no')
 
     # use BLAS/ATLAS
     if (ctx.options.enable_blas != False):
@@ -542,12 +554,14 @@
                 relative_trick = True)
 
 def sphinx(bld):
-    # build documentation from source files using sphinx-build
-    # note: build in ../doc/_build/html, otherwise waf wont install unsigned files
+    # build documentation from source files using sphinx-build note: build in
+    # ../doc/_build/html, otherwise waf wont install unsigned files
     if bld.env['SPHINX']:
         bld.env.VERSION = VERSION
         bld( name = 'sphinx',
-                rule = '${SPHINX} -b html -D release=${VERSION} -D version=${VERSION} -a -q `dirname ${SRC}` `dirname ${TGT}`',
+                rule = '${SPHINX} -b html -D release=${VERSION}' \
+                        ' -D version=${VERSION} -a -q' \
+                        ' `dirname ${SRC}` `dirname ${TGT}`',
                 source = 'doc/conf.py',
                 target = '../doc/_build/html/index.html')
         bld.install_files( '${DATAROOTDIR}' + '/doc/libaubio-doc/sphinx',
@@ -577,13 +591,16 @@
 def shutdown(bld):
     from waflib import Logs
     if bld.options.target_platform in ['ios', 'iosimulator']:
-        msg ='building for %s, contact the author for a commercial license' % bld.options.target_platform
+        msg ='building for %s, contact the author for a commercial license' \
+                % bld.options.target_platform
         Logs.pprint('RED', msg)
         msg ='   Paul Brossier <piem@aubio.org>'
         Logs.pprint('RED', msg)
 
 def dist(ctx):
-    ctx.excl  = ' **/.waf* **/*~ **/*.pyc **/*.swp **/*.swo **/*.swn **/.lock-w* **/.git*'
+    ctx.excl  = ' **/.waf*'
+    ctx.excl += ' **/.git*'
+    ctx.excl += ' **/*~ **/*.pyc **/*.swp **/*.swo **/*.swn **/.lock-w*'
     ctx.excl += ' **/build/*'
     ctx.excl += ' doc/_build'
     ctx.excl += ' python/demos_*'
@@ -591,6 +608,9 @@
     ctx.excl += ' **/python/ext/config.h'
     ctx.excl += ' **/python/lib/aubio/_aubio.so'
     ctx.excl += ' **.egg-info'
+    ctx.excl += ' **/.eggs'
+    ctx.excl += ' **/.pytest_cache'
+    ctx.excl += ' **/.cache'
     ctx.excl += ' **/**.zip **/**.tar.bz2'
     ctx.excl += ' **.tar.bz2'
     ctx.excl += ' **/doc/full/* **/doc/web/*'