Showing the changes to python (61 of 230 files). Show all
Unified · Split
| @@ -1,100 +1,109 @@ | |||
| 1 | Python aubio module | 1 | aubio |
| 2 | =================== | 2 | ===== |
| 3 | 3 | ||
| 4 | This module wraps the aubio library for Python using the numpy module. | 4 | aubio is a collection of tools for music and audio analysis. |
| 5 | 5 | ||
| 6 | Using the Python aubio module | 6 | This package integrates the aubio library with [NumPy] to provide a set of |
| 7 | ----------------------------- | 7 | efficient tools to process and analyse audio signals, including: |
| 8 | 8 | ||
| 9 | After installing python-aubio, you will be able to import the aubio module: | 9 | - read audio from any media file, including videos and remote streams |
| 10 | - high quality phase vocoder, spectral filterbanks, and linear filters | ||
| 11 | - Mel-Frequency Cepstrum Coefficients and standard spectral descriptors | ||
| 12 | - detection of note attacks (onset) | ||
| 13 | - pitch tracking (fundamental frequency estimation) | ||
| 14 | - beat detection and tempo tracking | ||
| 10 | 15 | ||
| 11 | $ python | 16 | aubio works with both Python 2 and Python 3. |
| 12 | [...] | ||
| 13 | >>> import aubio | ||
| 14 | >>> help(aubio.miditofreq) | ||
| 15 | 17 | ||
| 16 | Finding some inspiration | 18 | Links |
| 17 | ------------------------ | 19 | ----- |
| 18 | 20 | ||
| 19 | Some examples are available in the `python/demos` directory. These scripts are | 21 | - [module documentation][doc_python] |
| 20 | small programs written in python and using python-aubio. | 22 | - [installation instructions][doc_python_install] |
| 23 | - [aubio manual][manual] | ||
| 24 | - [aubio homepage][homepage] | ||
| 25 | - [issue tracker][bugtracker] | ||
| 21 | 26 | ||
| 22 | For instance, `demo_source.py` reads a media file. | 27 | Demos |
| 28 | ----- | ||
| 23 | 29 | ||
| 24 | $ ./python/demos/demo_source.py /path/to/sound/sample.wav | 30 | Some examples are available in the [`python/demos`][demos_dir] folder. Each |
| 31 | script is a command line program which accepts one ore more argument. | ||
| 25 | 32 | ||
| 26 | and `demo_timestretch_online.py` stretches the original file into a new one: | 33 | **Notes**: installing additional modules is required to run some of the demos. |
| 27 | 34 | ||
| 28 | $ ./python/demo/demo_timestretch_online.py loop.wav stretched_loop.wav 0.92` | 35 | ### Analysis |
| 29 | 36 | ||
| 30 | Note: you might need to install additional modules to run some of the demos. | 37 | - `demo_source.py` uses aubio to read audio samples from media files |
| 31 | Some demos use [matplotlib](http://matplotlib.org/) to draw plots, others use | 38 | - `demo_onset_plot.py` detects attacks in a sound file and plots the results |
| 32 | [PySoundCard](https://github.com/bastibe/PySoundCard) to play and record | 39 | using [matplotlib] |
| 33 | sounds. | 40 | - `demo_pitch.py` looks for fundamental frequency in a sound file and plots the |
| 41 | results using [matplotlib] | ||
| 42 | - `demo_spectrogram.py`, `demo_specdesc.py`, `demo_mfcc.py` for spectral | ||
| 43 | analysis. | ||
| 34 | 44 | ||
| 35 | Testing the Python module | 45 | ### Real-time |
| 36 | ------------------------- | ||
| 37 | 46 | ||
| 38 | To run the all the python tests, use the script: | 47 | - `demo_pyaudio.py` and `demo_tapthebeat.py` use [pyaudio] |
| 48 | - `demo_pysoundcard_play.py`, `demo_pysoundcard.py` use [PySoundCard] | ||
| 49 | - `demo_alsa.py` uses [pyalsaaudio] | ||
| 39 | 50 | ||
| 40 | $ ./python/tests/run_all_tests | 51 | ### Others |
| 41 | 52 | ||
| 42 | Each test script can also be called one at a time. For instance: | 53 | - `demo_timestretch.py` can change the duration of an input file and write the |
| 54 | new sound to disk, | ||
| 55 | - `demo_wav2midi.py` detects the notes in a file and uses [mido] to write the | ||
| 56 | results into a MIDI file | ||
| 43 | 57 | ||
| 44 | $ ./python/tests/test_note2midi.py -v | 58 | ### Example |
| 45 | 59 | ||
| 46 | Install in a virtualenv | 60 | Use `demo_timestretch_online.py` to slow down `loop.wav`, write the results in |
| 47 | ----------------------- | 61 | `stretched_loop.wav`: |
| 48 | 62 | ||
| 49 | You should be able to install python-aubio directly from the top source | 63 | $ python demo_timestretch_online.py loop.wav stretched_loop.wav 0.92 |
| 50 | directory of aubio. | ||
| 51 | 64 | ||
| 52 | First, create a virtualenv to hold the required python module: | 65 | Built with |
| 53 | |||
| 54 | $ virtualenv pyaubio | ||
| 55 | $ source pyaubio/bin/activate | ||
| 56 | |||
| 57 | Now install and build the python extension using: | ||
| 58 | |||
| 59 | $ pip install . | ||
| 60 | |||
| 61 | Install requirements | ||
| 62 | -------------------- | ||
| 63 | |||
| 64 | Before compiling this module, you must have compiled libaubio. | ||
| 65 | |||
| 66 | A simple way to do this is with pip: | ||
| 67 | |||
| 68 | $ pip install -r requirements.txt | ||
| 69 | |||
| 70 | For more information about how this module works, please refer to the [Python/C | ||
| 71 | API Reference Manual] (http://docs.python.org/c-api/index.html) and the | ||
| 72 | [Numpy/C API Reference](http://docs.scipy.org/doc/numpy/reference/c-api.html). | ||
| 73 | |||
| 74 | Compiling python aubio | ||
| 75 | ---------------------- | ||
| 76 | |||
| 77 | To build the aubio Python module, run the following command from the top source | ||
| 78 | directory of aubio: | ||
| 79 | |||
| 80 | $ ./setup.py build | ||
| 81 | |||
| 82 | Note: if libaubio was previously built using waf, the script will use it. | ||
| 83 | Otherwise, the entire library will be built inside the python extension. | ||
| 84 | |||
| 85 | To find out more about `setup.py` options: | ||
| 86 | |||
| 87 | $ ./setup.py --help | ||
| 88 | |||
| 89 | Installing | ||
| 90 | ---------- | 66 | ---------- |
| 91 | 67 | ||
| 92 | To install the Python module: | 68 | The core of aubio is written in C for portability and speed. In addition to |
| 93 | 69 | [NumPy], aubio can be optionally built to use one or more of the following | |
| 94 | $ ./setup.py install | 70 | libraries: |
| 95 | 71 | ||
| 96 | Alternatively, you may want to use the Python module without installing it by | 72 | - media file reading: |
| 97 | setting your PYTHONPATH, for instance as follows: | 73 | |
| 98 | 74 | - [ffmpeg] / [avcodec] to decode and read audio from almost any format, | |
| 99 | $ export PYTHONPATH=$PYTHONPATH:$PWD/`ls -rtd build/lib.* | head -1`:$PWD/tests | 75 | - [libsndfile] to read audio from uncompressed sound files, |
| 100 | 76 | - [libsamplerate] to re-sample audio signals, | |
| 77 | - [CoreAudio] to read all media formats supported by macOS, iOS, and tvOS. | ||
| 78 | |||
| 79 | - hardware acceleration: | ||
| 80 | |||
| 81 | - [Atlas] and [Blas], for accelerated vector and matrix computations, | ||
| 82 | - [fftw3], to compute fast Fourier Transforms of any size, | ||
| 83 | - [Accelerate] for accelerated FFT and matrix computations (macOS/iOS), | ||
| 84 | - [Intel IPP], accelerated vector computation and FFT implementation. | ||
| 85 | |||
| 86 | [ffmpeg]: https://ffmpeg.org | ||
| 87 | [avcodec]: https://libav.org | ||
| 88 | [libsndfile]: http://www.mega-nerd.com/libsndfile/ | ||
| 89 | [libsamplerate]: http://www.mega-nerd.com/SRC/ | ||
| 90 | [CoreAudio]: https://developer.apple.com/reference/coreaudio | ||
| 91 | [Atlas]: http://math-atlas.sourceforge.net/ | ||
| 92 | [Blas]: https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms | ||
| 93 | [fftw3]: http://fftw.org | ||
| 94 | [Accelerate]: https://developer.apple.com/reference/accelerate | ||
| 95 | [Intel IPP]: https://software.intel.com/en-us/intel-ipp | ||
| 96 | |||
| 97 | [demos_dir]:https://github.com/aubio/aubio/tree/master/python/demos | ||
| 98 | [pyaudio]:https://people.csail.mit.edu/hubert/pyaudio/ | ||
| 99 | [PySoundCard]:https://github.com/bastibe/PySoundCard | ||
| 100 | [pyalsaaudio]:https://larsimmisch.github.io/pyalsaaudio/ | ||
| 101 | [mido]:https://mido.readthedocs.io | ||
| 102 | |||
| 103 | [manual]: https://aubio.org/manual/latest/ | ||
| 104 | [doc_python]: https://aubio.org/manual/latest/python.html | ||
| 105 | [doc_python_install]: https://aubio.org/manual/latest/python_module.html | ||
| 106 | [homepage]: https://aubio.org | ||
| 107 | [NumPy]: https://www.numpy.org | ||
| 108 | [bugtracker]: https://github.com/aubio/aubio/issues | ||
| 109 | [matplotlib]:https://matplotlib.org/ | ||
Empty file deleted.
| @@ -0,0 +1,45 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | import alsaaudio | ||
| 4 | import numpy as np | ||
| 5 | import aubio | ||
| 6 | |||
| 7 | # constants | ||
| 8 | samplerate = 44100 | ||
| 9 | win_s = 2048 | ||
| 10 | hop_s = win_s // 2 | ||
| 11 | framesize = hop_s | ||
| 12 | |||
| 13 | # set up audio input | ||
| 14 | recorder = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE) | ||
| 15 | recorder.setperiodsize(framesize) | ||
| 16 | recorder.setrate(samplerate) | ||
| 17 | recorder.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE) | ||
| 18 | recorder.setchannels(1) | ||
| 19 | |||
| 20 | # create aubio pitch detection (first argument is method, "default" is | ||
| 21 | # "yinfft", can also be "yin", "mcomb", fcomb", "schmitt"). | ||
| 22 | pitcher = aubio.pitch("default", win_s, hop_s, samplerate) | ||
| 23 | # set output unit (can be 'midi', 'cent', 'Hz', ...) | ||
| 24 | pitcher.set_unit("Hz") | ||
| 25 | # ignore frames under this level (dB) | ||
| 26 | pitcher.set_silence(-40) | ||
| 27 | |||
| 28 | print("Starting to listen, press Ctrl+C to stop") | ||
| 29 | |||
| 30 | # main loop | ||
| 31 | while True: | ||
| 32 | try: | ||
| 33 | # read data from audio input | ||
| 34 | _, data = recorder.read() | ||
| 35 | # convert data to aubio float samples | ||
| 36 | samples = np.fromstring(data, dtype=aubio.float_type) | ||
| 37 | # pitch of current frame | ||
| 38 | freq = pitcher(samples)[0] | ||
| 39 | # compute energy of current block | ||
| 40 | energy = np.sum(samples**2)/len(samples) | ||
| 41 | # do something with the results | ||
| 42 | print("{:10.4f} {:10.4f}".format(freq,energy)) | ||
| 43 | except KeyboardInterrupt: | ||
| 44 | print("Ctrl+C pressed, exiting") | ||
| 45 | break | ||
| @@ -0,0 +1,50 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | import numpy as np | ||
| 4 | from aubio import pitch | ||
| 5 | import pylab as plt | ||
| 6 | |||
| 7 | buf_size = 2048 * 1 | ||
| 8 | hop_size = buf_size // 4 | ||
| 9 | |||
| 10 | samplerate = 44100 | ||
| 11 | minfreq = 40 | ||
| 12 | maxfreq = 6000 | ||
| 13 | |||
| 14 | def sinewave(freq, duration, samplerate = samplerate): | ||
| 15 | """ generate a sinewave """ | ||
| 16 | length = hop_size | ||
| 17 | while length < duration * samplerate: | ||
| 18 | length += hop_size | ||
| 19 | return np.sin( 2. * np.pi * np.arange(length) * freq / samplerate ).astype("float32") | ||
| 20 | |||
| 21 | def get_stats_for_pitch_method(method, freqs, samplerate = samplerate): | ||
| 22 | """ for a given pitch method and a list of frequency, generate a sinewave | ||
| 23 | and get mean deviation """ | ||
| 24 | means = np.zeros(len(freqs)) | ||
| 25 | medians = np.zeros(len(freqs)) | ||
| 26 | for freq, fn in zip(freqs, range(len(freqs))): | ||
| 27 | s = sinewave(freq, .50).reshape(-1, hop_size) | ||
| 28 | #s = (sinewave(freq, .50) + .0*sinewave(freq/2., .50)).reshape(-1, hop_size) | ||
| 29 | p = pitch(method, buf_size, hop_size, samplerate = samplerate) | ||
| 30 | candidates = np.zeros(len(s)) | ||
| 31 | #samples = np.zeros(buf_size) | ||
| 32 | for frame, i in zip(s, range(len(s))): | ||
| 33 | candidates[i] = p(frame)[0] | ||
| 34 | # skip first few candidates | ||
| 35 | candidates = candidates[4:] | ||
| 36 | means[fn] = np.mean(candidates[candidates != 0] - freq) | ||
| 37 | medians[fn] = np.median(candidates[candidates != 0] - freq) | ||
| 38 | print (freq, means[fn], medians[fn]) | ||
| 39 | return means, medians | ||
| 40 | |||
| 41 | if __name__ == '__main__': | ||
| 42 | freqs = np.arange(minfreq, maxfreq, 1.) | ||
| 43 | modes = ["yin", "yinfft"] | ||
| 44 | for mode in modes: | ||
| 45 | means, medians = get_stats_for_pitch_method(mode, freqs) | ||
| 46 | plt.figure() | ||
| 47 | plt.plot(freqs, means, 'g-') | ||
| 48 | plt.plot(freqs, medians, 'r--') | ||
| 49 | #plt.savefig(mode + '_deviations_test.png', dpi=300) | ||
| 50 | plt.show() | ||
| @@ -3,26 +3,33 @@ · 2 unmodified lines | |||
| 3 | from aubio import source, tempo | 3 | from aubio import source, tempo |
| 4 | from numpy import median, diff | 4 | from numpy import median, diff |
| 5 | 5 | ||
| 6 | def get_file_bpm(path, params = None): | 6 | def get_file_bpm(path, params=None): |
| 7 | """ Calculate the beats per minute (bpm) of a given file. | 7 | """ Calculate the beats per minute (bpm) of a given file. |
| 8 | path: path to the file | 8 | path: path to the file |
| 9 | param: dictionary of parameters | 9 | param: dictionary of parameters |
| 10 | """ | 10 | """ |
| 11 | if params is None: | 11 | if params is None: |
| 12 | params = {} | 12 | params = {} |
| 13 | try: | 13 | # default: |
| 14 | win_s = params['win_s'] | 14 | samplerate, win_s, hop_s = 44100, 1024, 512 |
| 15 | samplerate = params['samplerate'] | 15 | if 'mode' in params: |
| 16 | hop_s = params['hop_s'] | 16 | if params.mode in ['super-fast']: |
| 17 | except KeyError: | 17 | # super fast |
| 18 | """ | 18 | samplerate, win_s, hop_s = 4000, 128, 64 |
| 19 | # super fast | 19 | elif params.mode in ['fast']: |
| 20 | samplerate, win_s, hop_s = 4000, 128, 64 | 20 | # fast |
| 21 | # fast | 21 | samplerate, win_s, hop_s = 8000, 512, 128 |
| 22 | samplerate, win_s, hop_s = 8000, 512, 128 | 22 | elif params.mode in ['default']: |
| 23 | """ | 23 | pass |
| 24 | # default: | 24 | else: |
| 25 | samplerate, win_s, hop_s = 44100, 1024, 512 | 25 | raise ValueError("unknown mode {:s}".format(params.mode)) |
| 26 | # manual settings | ||
| 27 | if 'samplerate' in params: | ||
| 28 | samplerate = params.samplerate | ||
| 29 | if 'win_s' in params: | ||
| 30 | win_s = params.win_s | ||
| 31 | if 'hop_s' in params: | ||
| 32 | hop_s = params.hop_s | ||
| 26 | 33 | ||
| 27 | s = source(path, samplerate, hop_s) | 34 | s = source(path, samplerate, hop_s) |
| 28 | samplerate = s.samplerate | 35 | samplerate = s.samplerate |
| @@ -44,19 +51,29 @@ def get_file_bpm(path, params = None): · 15 unmodified lines | |||
| 44 | if read < hop_s: | 51 | if read < hop_s: |
| 45 | break | 52 | break |
| 46 | 53 | ||
| 47 | # Convert to periods and to bpm | 54 | def beats_to_bpm(beats, path): |
| 48 | if len(beats) > 1: | 55 | # if enough beats are found, convert to periods then to bpm |
| 49 | if len(beats) < 4: | 56 | if len(beats) > 1: |
| 50 | print("few beats found in {:s}".format(path)) | 57 | if len(beats) < 4: |
| 51 | bpms = 60./diff(beats) | 58 | print("few beats found in {:s}".format(path)) |
| 52 | b = median(bpms) | 59 | bpms = 60./diff(beats) |
| 53 | else: | 60 | return median(bpms) |
| 54 | b = 0 | 61 | else: |
| 55 | print("not enough beats found in {:s}".format(path)) | 62 | print("not enough beats found in {:s}".format(path)) |
| 56 | return b | 63 | return 0 |
| 64 | |||
| 65 | return beats_to_bpm(beats, path) | ||
| 57 | 66 | ||
| 58 | if __name__ == '__main__': | 67 | if __name__ == '__main__': |
| 59 | import sys | 68 | import argparse |
| 60 | for f in sys.argv[1:]: | 69 | parser = argparse.ArgumentParser() |
| 61 | bpm = get_file_bpm(f) | 70 | parser.add_argument('-m', '--mode', |
| 71 | help="mode [default|fast|super-fast]", | ||
| 72 | dest="mode", default='default') | ||
| 73 | parser.add_argument('sources', | ||
| 74 | nargs='+', | ||
| 75 | help="input_files") | ||
| 76 | args = parser.parse_args() | ||
| 77 | for f in args.sources: | ||
| 78 | bpm = get_file_bpm(f, params = args) | ||
| 62 | print("{:6s} {:s}".format("{:2f}".format(bpm), f)) | 79 | print("{:6s} {:s}".format("{:2f}".format(bpm), f)) |
| @@ -1,36 +1,53 @@ | |||
| 1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
| 2 | 2 | ||
| 3 | import sys | ||
| 4 | import os.path | ||
| 5 | import aubio | ||
| 3 | 6 | ||
| 4 | def apply_filter(path): | ||
| 5 | from aubio import source, sink, digital_filter | ||
| 6 | from os.path import basename, splitext | ||
| 7 | 7 | ||
| 8 | def apply_filter(path, target): | ||
| 8 | # open input file, get its samplerate | 9 | # open input file, get its samplerate |
| 9 | s = source(path) | 10 | s = aubio.source(path) |
| 10 | samplerate = s.samplerate | 11 | samplerate = s.samplerate |
| 11 | 12 | ||
| 12 | # create an A-weighting filter | 13 | # create an A-weighting filter |
| 13 | f = digital_filter(7) | 14 | f = aubio.digital_filter(7) |
| 14 | f.set_a_weighting(samplerate) | 15 | f.set_a_weighting(samplerate) |
| 15 | # alternatively, apply another filter | ||
| 16 | 16 | ||
| 17 | # create output file | 17 | # create output file |
| 18 | o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate) | 18 | o = aubio.sink(target, samplerate) |
| 19 | 19 | ||
| 20 | total_frames = 0 | 20 | total_frames = 0 |
| 21 | while True: | 21 | while True: |
| 22 | # read from source | ||
| 22 | samples, read = s() | 23 | samples, read = s() |
| 24 | # filter samples | ||
| 23 | filtered_samples = f(samples) | 25 | filtered_samples = f(samples) |
| 26 | # write to sink | ||
| 24 | o(filtered_samples, read) | 27 | o(filtered_samples, read) |
| 28 | # count frames read | ||
| 25 | total_frames += read | 29 | total_frames += read |
| 26 | if read < s.hop_size: break | 30 | # end of file reached |
| 31 | if read < s.hop_size: | ||
| 32 | break | ||
| 27 | 33 | ||
| 34 | # print some info | ||
| 28 | duration = total_frames / float(samplerate) | 35 | duration = total_frames / float(samplerate) |
| 29 | print ("read {:s}".format(s.uri)) | 36 | input_str = "input: {:s} ({:.2f} s, {:d} Hz)" |
| 30 | print ("applied A-weighting filtered ({:d} Hz)".format(samplerate)) | 37 | output_str = "output: {:s}, A-weighting filtered ({:d} frames total)" |
| 31 | print ("wrote {:s} ({:.2f} s)".format(o.uri, duration)) | 38 | print(input_str.format(s.uri, duration, samplerate)) |
| 39 | print(output_str.format(o.uri, total_frames)) | ||
| 32 | 40 | ||
| 33 | if __name__ == '__main__': | 41 | if __name__ == '__main__': |
| 34 | import sys | 42 | usage = "{:s} <input_file> [output_file]".format(sys.argv[0]) |
| 35 | for f in sys.argv[1:]: | 43 | if not 1 < len(sys.argv) < 4: |
| 36 | apply_filter(f) | 44 | print(usage) |
| 45 | sys.exit(1) | ||
| 46 | if len(sys.argv) < 3: | ||
| 47 | input_path = sys.argv[1] | ||
| 48 | basename = os.path.splitext(os.path.basename(input_path))[0] + ".wav" | ||
| 49 | output_path = "filtered_" + basename | ||
| 50 | else: | ||
| 51 | input_path, output_path = sys.argv[1:] | ||
| 52 | # run function | ||
| 53 | apply_filter(input_path, output_path) | ||
| @@ -1,30 +1,44 @@ | |||
| 1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
| 2 | 2 | ||
| 3 | from aubio import filterbank, fvec | 3 | """Create a filterbank from a list of frequencies. |
| 4 | from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title | ||
| 5 | from numpy import vstack, arange | ||
| 6 | 4 | ||
| 7 | win_s = 2048 | 5 | This demo uses `aubio.filterbank.set_triangle_bands` to build a set of |
| 6 | triangular filters from a list of frequencies. | ||
| 7 | |||
| 8 | The filterbank coefficients are then modified before being displayed.""" | ||
| 9 | |||
| 10 | import aubio | ||
| 11 | import numpy as np | ||
| 12 | import matplotlib.pyplot as plt | ||
| 13 | |||
| 14 | # sampling rate and size of the fft | ||
| 8 | samplerate = 48000 | 15 | samplerate = 48000 |
| 16 | win_s = 2048 | ||
| 9 | 17 | ||
| 18 | # define a list of custom frequency | ||
| 10 | freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000] | 19 | freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000] |
| 20 | # number of filters to create | ||
| 11 | n_filters = len(freq_list) - 2 | 21 | n_filters = len(freq_list) - 2 |
| 12 | 22 | ||
| 13 | f = filterbank(n_filters, win_s) | 23 | # create a new filterbank |
| 14 | freqs = fvec(freq_list) | 24 | f = aubio.filterbank(n_filters, win_s) |
| 25 | freqs = aubio.fvec(freq_list) | ||
| 15 | f.set_triangle_bands(freqs, samplerate) | 26 | f.set_triangle_bands(freqs, samplerate) |
| 16 | 27 | ||
| 28 | # get the coefficients from the filterbank | ||
| 17 | coeffs = f.get_coeffs() | 29 | coeffs = f.get_coeffs() |
| 18 | coeffs[4] *= 5. | 30 | # apply a gain to fifth band |
| 19 | 31 | coeffs[4] *= 6. | |
| 32 | # load the modified coeffs into the filterbank | ||
| 20 | f.set_coeffs(coeffs) | 33 | f.set_coeffs(coeffs) |
| 21 | 34 | ||
| 22 | times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) | 35 | # display the band gains in a loglog plot |
| 23 | title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.') | 36 | freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) |
| 24 | loglog(times.T, f.get_coeffs().T, '.-') | 37 | plt.title('filterbank built from a list of frequencies\n' |
| 25 | xlim([50, samplerate/2]) | 38 | 'The 5th band has been amplified by a factor 6.') |
| 26 | ylim([1.0e-6, 2.0e-2]) | 39 | plt.loglog(freqs.T, f.get_coeffs().T, '.-') |
| 27 | xlabel('log frequency (Hz)') | 40 | plt.xlim([50, samplerate/2]) |
| 28 | ylabel('log amplitude') | 41 | plt.ylim([1.0e-6, 2.0e-2]) |
| 29 | 42 | plt.xlabel('log frequency (Hz)') | |
| 30 | show() | 43 | plt.ylabel('log amplitude') |
| 44 | plt.show() | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | import sys | ||
| 4 | from aubio import source, notes | ||
| 5 | |||
| 6 | if len(sys.argv) < 2: | ||
| 7 | print("Usage: %s <filename> [samplerate]" % sys.argv[0]) | ||
| 8 | sys.exit(1) | ||
| 9 | |||
| 10 | filename = sys.argv[1] | ||
| 11 | |||
| 12 | downsample = 1 | ||
| 13 | samplerate = 44100 // downsample | ||
| 14 | if len( sys.argv ) > 2: samplerate = int(sys.argv[2]) | ||
| 15 | |||
| 16 | win_s = 512 // downsample # fft size | ||
| 17 | hop_s = 256 // downsample # hop size | ||
| 18 | |||
| 19 | s = source(filename, samplerate, hop_s) | ||
| 20 | samplerate = s.samplerate | ||
| 21 | |||
| 22 | tolerance = 0.8 | ||
| 23 | |||
| 24 | notes_o = notes("default", win_s, hop_s, samplerate) | ||
| 25 | |||
| 26 | print("%8s" % "time","[ start","vel","last ]") | ||
| 27 | |||
| 28 | # total number of frames read | ||
| 29 | total_frames = 0 | ||
| 30 | while True: | ||
| 31 | samples, read = s() | ||
| 32 | new_note = notes_o(samples) | ||
| 33 | if (new_note[0] != 0): | ||
| 34 | note_str = ' '.join(["%.2f" % i for i in new_note]) | ||
| 35 | print("%.6f" % (total_frames/float(samplerate)), new_note) | ||
| 36 | total_frames += read | ||
| 37 | if read < hop_s: break | ||
| @@ -37,7 +37,7 @@ freqs[ pointer : pointer + partition ] = 1480 · 36 unmodified lines | |||
| 37 | 37 | ||
| 38 | pointer += partition | 38 | pointer += partition |
| 39 | pointer += partition | 39 | pointer += partition |
| 40 | freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length/8) | 40 | freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length//8) |
| 41 | 41 | ||
| 42 | a = build_sinusoid(sin_length, freqs, samplerate) | 42 | a = build_sinusoid(sin_length, freqs, samplerate) |
| 43 | 43 | ||
| @@ -0,0 +1,75 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Use pyaudio to open the microphone and run aubio.pitch on the stream of | ||
| 4 | # incoming samples. If a filename is given as the first argument, it will | ||
| 5 | # record 5 seconds of audio to this location. Otherwise, the script will | ||
| 6 | # run until Ctrl+C is pressed. | ||
| 7 | |||
| 8 | # Examples: | ||
| 9 | # $ ./python/demos/demo_pyaudio.py | ||
| 10 | # $ ./python/demos/demo_pyaudio.py /tmp/recording.wav | ||
| 11 | |||
| 12 | import pyaudio | ||
| 13 | import sys | ||
| 14 | import numpy as np | ||
| 15 | import aubio | ||
| 16 | |||
| 17 | # initialise pyaudio | ||
| 18 | p = pyaudio.PyAudio() | ||
| 19 | |||
| 20 | # open stream | ||
| 21 | buffer_size = 1024 | ||
| 22 | pyaudio_format = pyaudio.paFloat32 | ||
| 23 | n_channels = 1 | ||
| 24 | samplerate = 44100 | ||
| 25 | stream = p.open(format=pyaudio_format, | ||
| 26 | channels=n_channels, | ||
| 27 | rate=samplerate, | ||
| 28 | input=True, | ||
| 29 | frames_per_buffer=buffer_size) | ||
| 30 | |||
| 31 | if len(sys.argv) > 1: | ||
| 32 | # record 5 seconds | ||
| 33 | output_filename = sys.argv[1] | ||
| 34 | record_duration = 5 # exit 1 | ||
| 35 | outputsink = aubio.sink(sys.argv[1], samplerate) | ||
| 36 | total_frames = 0 | ||
| 37 | else: | ||
| 38 | # run forever | ||
| 39 | outputsink = None | ||
| 40 | record_duration = None | ||
| 41 | |||
| 42 | # setup pitch | ||
| 43 | tolerance = 0.8 | ||
| 44 | win_s = 4096 # fft size | ||
| 45 | hop_s = buffer_size # hop size | ||
| 46 | pitch_o = aubio.pitch("default", win_s, hop_s, samplerate) | ||
| 47 | pitch_o.set_unit("midi") | ||
| 48 | pitch_o.set_tolerance(tolerance) | ||
| 49 | |||
| 50 | print("*** starting recording") | ||
| 51 | while True: | ||
| 52 | try: | ||
| 53 | audiobuffer = stream.read(buffer_size) | ||
| 54 | signal = np.fromstring(audiobuffer, dtype=np.float32) | ||
| 55 | |||
| 56 | pitch = pitch_o(signal)[0] | ||
| 57 | confidence = pitch_o.get_confidence() | ||
| 58 | |||
| 59 | print("{} / {}".format(pitch,confidence)) | ||
| 60 | |||
| 61 | if outputsink: | ||
| 62 | outputsink(signal, len(signal)) | ||
| 63 | |||
| 64 | if record_duration: | ||
| 65 | total_frames += len(signal) | ||
| 66 | if record_duration * samplerate < total_frames: | ||
| 67 | break | ||
| 68 | except KeyboardInterrupt: | ||
| 69 | print("*** Ctrl+C pressed, exiting") | ||
| 70 | break | ||
| 71 | |||
| 72 | print("*** done recording") | ||
| 73 | stream.stop_stream() | ||
| 74 | stream.close() | ||
| 75 | p.terminate() | ||
| @@ -1,16 +1,20 @@ | |||
| 1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
| 2 | import sys, aubio | 2 | |
| 3 | """A simple example using aubio.source.""" | ||
| 4 | |||
| 5 | import sys | ||
| 6 | import aubio | ||
| 3 | 7 | ||
| 4 | samplerate = 0 # use original source samplerate | 8 | samplerate = 0 # use original source samplerate |
| 5 | hop_size = 256 # number of frames to read in one block | 9 | hop_size = 256 # number of frames to read in one block |
| 6 | s = aubio.source(sys.argv[1], samplerate, hop_size) | 10 | src = aubio.source(sys.argv[1], samplerate, hop_size) |
| 7 | total_frames = 0 | 11 | total_frames = 0 |
| 8 | 12 | ||
| 9 | while True: # reading loop | 13 | while True: |
| 10 | samples, read = s() | 14 | samples, read = src() # read hop_size new samples from source |
| 11 | total_frames += read | 15 | total_frames += read # increment total number of frames |
| 12 | if read < hop_size: break # end of file reached | 16 | if read < hop_size: # end of file reached |
| 17 | break | ||
| 13 | 18 | ||
| 14 | fmt_string = "read {:d} frames at {:d}Hz from {:s}" | 19 | fmt_string = "read {:d} frames at {:d}Hz from {:s}" |
| 15 | print (fmt_string.format(total_frames, s.samplerate, sys.argv[1])) | 20 | print(fmt_string.format(total_frames, src.samplerate, src.uri)) |
| 16 | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """ A simple demo using aubio and pyaudio to play beats in real time | ||
| 4 | |||
| 5 | Note you will need to have pyaudio installed: `pip install pyaudio`. | ||
| 6 | |||
| 7 | Examples: | ||
| 8 | ./demo_tapthebeat.py ~/Music/track1.ogg | ||
| 9 | |||
| 10 | When compiled with ffmpeg/libav, you should be able to open remote streams. For | ||
| 11 | instance using youtube-dl (`pip install youtube-dl`): | ||
| 12 | |||
| 13 | ./demo_tapthebeat.py `youtube-dl -xg https://youtu.be/zZbM9n9j3_g` | ||
| 14 | |||
| 15 | """ | ||
| 16 | |||
| 17 | import sys | ||
| 18 | import time | ||
| 19 | import pyaudio | ||
| 20 | import aubio | ||
| 21 | import numpy as np | ||
| 22 | |||
| 23 | win_s = 1024 # fft size | ||
| 24 | hop_s = win_s // 2 # hop size | ||
| 25 | |||
| 26 | # parse command line arguments | ||
| 27 | if len(sys.argv) < 2: | ||
| 28 | print("Usage: %s <filename> [samplerate]" % sys.argv[0]) | ||
| 29 | sys.exit(1) | ||
| 30 | |||
| 31 | filename = sys.argv[1] | ||
| 32 | |||
| 33 | samplerate = 0 | ||
| 34 | if len( sys.argv ) > 2: samplerate = int(sys.argv[2]) | ||
| 35 | |||
| 36 | # create aubio source | ||
| 37 | a_source = aubio.source(filename, samplerate, hop_s) | ||
| 38 | samplerate = a_source.samplerate | ||
| 39 | |||
| 40 | # create aubio tempo detection | ||
| 41 | a_tempo = aubio.tempo("default", win_s, hop_s, samplerate) | ||
| 42 | |||
| 43 | # create a simple click sound | ||
| 44 | click = 0.7 * np.sin(2. * np.pi * np.arange(hop_s) / hop_s * samplerate / 3000.) | ||
| 45 | |||
| 46 | # pyaudio callback | ||
| 47 | def pyaudio_callback(_in_data, _frame_count, _time_info, _status): | ||
| 48 | samples, read = a_source() | ||
| 49 | is_beat = a_tempo(samples) | ||
| 50 | if is_beat: | ||
| 51 | samples += click | ||
| 52 | #print ('tick') # avoid print in audio callback | ||
| 53 | audiobuf = samples.tobytes() | ||
| 54 | if read < hop_s: | ||
| 55 | return (audiobuf, pyaudio.paComplete) | ||
| 56 | return (audiobuf, pyaudio.paContinue) | ||
| 57 | |||
| 58 | # create pyaudio stream with frames_per_buffer=hop_s and format=paFloat32 | ||
| 59 | p = pyaudio.PyAudio() | ||
| 60 | pyaudio_format = pyaudio.paFloat32 | ||
| 61 | frames_per_buffer = hop_s | ||
| 62 | n_channels = 1 | ||
| 63 | stream = p.open(format=pyaudio_format, channels=n_channels, rate=samplerate, | ||
| 64 | output=True, frames_per_buffer=frames_per_buffer, | ||
| 65 | stream_callback=pyaudio_callback) | ||
| 66 | |||
| 67 | # start pyaudio stream | ||
| 68 | stream.start_stream() | ||
| 69 | |||
| 70 | # wait for stream to finish | ||
| 71 | while stream.is_active(): | ||
| 72 | time.sleep(0.1) | ||
| 73 | |||
| 74 | # stop pyaudio stream | ||
| 75 | stream.stop_stream() | ||
| 76 | stream.close() | ||
| 77 | # close pyaudio | ||
| 78 | p.terminate() | ||
| @@ -12,7 +12,7 @@ from aubio import unwrap2pi, float_type · 11 unmodified lines | |||
| 12 | import numpy as np | 12 | import numpy as np |
| 13 | 13 | ||
| 14 | win_s = 1024 | 14 | win_s = 1024 |
| 15 | hop_s = win_s / 8 # 87.5 % overlap | 15 | hop_s = win_s // 8 # 87.5 % overlap |
| 16 | 16 | ||
| 17 | warmup = win_s // hop_s - 1 | 17 | warmup = win_s // hop_s - 1 |
| 18 | 18 | ||
| @@ -11,8 +11,8 @@ from aubio import source, sink, pvoc, cvec · 10 unmodified lines | |||
| 11 | from aubio import unwrap2pi, float_type | 11 | from aubio import unwrap2pi, float_type |
| 12 | import numpy as np | 12 | import numpy as np |
| 13 | 13 | ||
| 14 | win_s = 1024 | 14 | win_s = 512 |
| 15 | hop_s = win_s / 8 # 87.5 % overlap | 15 | hop_s = win_s // 8 # 87.5 % overlap |
| 16 | 16 | ||
| 17 | warmup = win_s // hop_s - 1 | 17 | warmup = win_s // hop_s - 1 |
| 18 | 18 | ||
| @@ -92,8 +92,10 @@ while True: · 73 unmodified lines | |||
| 92 | old_grain.norm = np.copy(cur_grain.norm) | 92 | old_grain.norm = np.copy(cur_grain.norm) |
| 93 | old_grain.phas = np.copy(cur_grain.phas) | 93 | old_grain.phas = np.copy(cur_grain.phas) |
| 94 | 94 | ||
| 95 | block_read += 1 | 95 | # until end of file |
| 96 | if read < hop_s: break | 96 | if read < hop_s: break |
| 97 | # increment block counter | ||
| 98 | block_read += 1 | ||
| 97 | 99 | ||
| 98 | for t in range(warmup + 2): # purge the last frames from the phase vocoder | 100 | for t in range(warmup + 2): # purge the last frames from the phase vocoder |
| 99 | new_grain.norm[:] = 0 | 101 | new_grain.norm[:] = 0 |
| @@ -0,0 +1,77 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Simple demo to extract notes from a sound file, and store them in a midi file | ||
| 4 | # using mido. | ||
| 5 | # | ||
| 6 | # Install mido: `pip instal mido` | ||
| 7 | # | ||
| 8 | # Documentation: https://mido.readthedocs.io/ | ||
| 9 | |||
| 10 | import sys | ||
| 11 | from aubio import source, notes | ||
| 12 | from mido import Message, MetaMessage, MidiFile, MidiTrack, second2tick, bpm2tempo | ||
| 13 | |||
| 14 | if len(sys.argv) < 3: | ||
| 15 | print("Usage: %s <filename> <output> [samplerate]" % sys.argv[0]) | ||
| 16 | sys.exit(1) | ||
| 17 | |||
| 18 | filename = sys.argv[1] | ||
| 19 | midioutput = sys.argv[2] | ||
| 20 | |||
| 21 | downsample = 1 | ||
| 22 | samplerate = 44100 // downsample | ||
| 23 | if len( sys.argv ) > 3: samplerate = int(sys.argv[3]) | ||
| 24 | |||
| 25 | win_s = 512 // downsample # fft size | ||
| 26 | hop_s = 256 // downsample # hop size | ||
| 27 | |||
| 28 | s = source(filename, samplerate, hop_s) | ||
| 29 | samplerate = s.samplerate | ||
| 30 | |||
| 31 | tolerance = 0.8 | ||
| 32 | |||
| 33 | notes_o = notes("default", win_s, hop_s, samplerate) | ||
| 34 | |||
| 35 | print("%8s" % "time","[ start","vel","last ]") | ||
| 36 | |||
| 37 | # create a midi file | ||
| 38 | mid = MidiFile() | ||
| 39 | track = MidiTrack() | ||
| 40 | mid.tracks.append(track) | ||
| 41 | |||
| 42 | ticks_per_beat = mid.ticks_per_beat # default: 480 | ||
| 43 | bpm = 120 # default midi tempo | ||
| 44 | |||
| 45 | tempo = bpm2tempo(bpm) | ||
| 46 | track.append(MetaMessage('set_tempo', tempo=tempo)) | ||
| 47 | track.append(MetaMessage('time_signature', numerator=4, denominator=4)) | ||
| 48 | |||
| 49 | def frames2tick(frames, samplerate=samplerate): | ||
| 50 | sec = frames / float(samplerate) | ||
| 51 | return int(second2tick(sec, ticks_per_beat, tempo)) | ||
| 52 | |||
| 53 | last_time = 0 | ||
| 54 | |||
| 55 | # total number of frames read | ||
| 56 | total_frames = 0 | ||
| 57 | while True: | ||
| 58 | samples, read = s() | ||
| 59 | new_note = notes_o(samples) | ||
| 60 | if (new_note[0] != 0): | ||
| 61 | note_str = ' '.join(["%.2f" % i for i in new_note]) | ||
| 62 | print("%.6f" % (total_frames/float(samplerate)), new_note) | ||
| 63 | delta = frames2tick(total_frames) - last_time | ||
| 64 | if new_note[2] > 0: | ||
| 65 | track.append(Message('note_off', note=int(new_note[2]), | ||
| 66 | velocity=127, time=0) | ||
| 67 | ) | ||
| 68 | track.append(Message('note_on', | ||
| 69 | note=int(new_note[0]), | ||
| 70 | velocity=int(new_note[1]), | ||
| 71 | time=delta) | ||
| 72 | ) | ||
| 73 | last_time = frames2tick(total_frames) | ||
| 74 | total_frames += read | ||
| 75 | if read < hop_s: break | ||
| 76 | |||
| 77 | mid.save(midioutput) | ||
| @@ -0,0 +1,175 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | # -*- coding: utf8 -*- | ||
| 3 | |||
| 4 | """ Pure python implementation of the sum of squared difference | ||
| 5 | |||
| 6 | sqd_yin: original sum of squared difference [0] | ||
| 7 | d_t(tau) = x ⊗ kernel | ||
| 8 | sqd_yinfast: sum of squared diff using complex domain [0] | ||
| 9 | sqd_yinfftslow: tappered squared diff [1] | ||
| 10 | sqd_yinfft: modified squared diff using complex domain [1] | ||
| 11 | |||
| 12 | [0]:http://audition.ens.fr/adc/pdf/2002_JASA_YIN.pdf | ||
| 13 | [1]:https://aubio.org/phd/ | ||
| 14 | """ | ||
| 15 | |||
| 16 | import sys | ||
| 17 | import numpy as np | ||
| 18 | import matplotlib.pyplot as plt | ||
| 19 | |||
| 20 | def sqd_yin(samples): | ||
| 21 | """ compute original sum of squared difference | ||
| 22 | |||
| 23 | Brute-force computation (cost o(N**2), slow).""" | ||
| 24 | B = len(samples) | ||
| 25 | W = B//2 | ||
| 26 | yin = np.zeros(W) | ||
| 27 | for j in range(W): | ||
| 28 | for tau in range(1, W): | ||
| 29 | yin[tau] += (samples[j] - samples[j+tau])**2 | ||
| 30 | return yin | ||
| 31 | |||
| 32 | def sqd_yinfast(samples): | ||
| 33 | """ compute approximate sum of squared difference | ||
| 34 | |||
| 35 | Using complex convolution (fast, cost o(n*log(n)) )""" | ||
| 36 | # yin_t(tau) = (r_t(0) + r_(t+tau)(0)) - 2r_t(tau) | ||
| 37 | B = len(samples) | ||
| 38 | W = B//2 | ||
| 39 | yin = np.zeros(W) | ||
| 40 | sqdiff = np.zeros(W) | ||
| 41 | kernel = np.zeros(B) | ||
| 42 | # compute r_(t+tau)(0) | ||
| 43 | squares = samples**2 | ||
| 44 | for tau in range(W): | ||
| 45 | sqdiff[tau] = squares[tau:tau+W].sum() | ||
| 46 | # add r_t(0) | ||
| 47 | sqdiff += sqdiff[0] | ||
| 48 | # compute r_t(tau) using kernel convolution in complex domain | ||
| 49 | samples_fft = np.fft.fft(samples) | ||
| 50 | kernel[1:W+1] = samples[W-1::-1] # first half, reversed | ||
| 51 | kernel_fft = np.fft.fft(kernel) | ||
| 52 | r_t_tau = np.fft.ifft(samples_fft * kernel_fft).real[W:] | ||
| 53 | # compute yin_t(tau) | ||
| 54 | yin = sqdiff - 2 * r_t_tau | ||
| 55 | return yin | ||
| 56 | |||
| 57 | def sqd_yintapered(samples): | ||
| 58 | """ compute tappered sum of squared difference | ||
| 59 | |||
| 60 | Brute-force computation (cost o(N**2), slow).""" | ||
| 61 | B = len(samples) | ||
| 62 | W = B//2 | ||
| 63 | yin = np.zeros(W) | ||
| 64 | for tau in range(1, W): | ||
| 65 | for j in range(W - tau): | ||
| 66 | yin[tau] += (samples[j] - samples[j+tau])**2 | ||
| 67 | return yin | ||
| 68 | |||
| 69 | def sqd_yinfft(samples): | ||
| 70 | """ compute yinfft modified sum of squared differences | ||
| 71 | |||
| 72 | Very fast, improved performance in transients. | ||
| 73 | |||
| 74 | FIXME: biased.""" | ||
| 75 | B = len(samples) | ||
| 76 | W = B//2 | ||
| 77 | yin = np.zeros(W) | ||
| 78 | def hanningz(W): | ||
| 79 | return .5 * (1. - np.cos(2. * np.pi * np.arange(W) / W)) | ||
| 80 | #win = np.ones(B) | ||
| 81 | win = hanningz(B) | ||
| 82 | sqrmag = np.zeros(B) | ||
| 83 | fftout = np.fft.fft(win*samples) | ||
| 84 | sqrmag[0] = fftout[0].real**2 | ||
| 85 | for l in range(1, W): | ||
| 86 | sqrmag[l] = fftout[l].real**2 + fftout[l].imag**2 | ||
| 87 | sqrmag[B-l] = sqrmag[l] | ||
| 88 | sqrmag[W] = fftout[W].real**2 | ||
| 89 | fftout = np.fft.fft(sqrmag) | ||
| 90 | sqrsum = 2.*sqrmag[:W + 1].sum() | ||
| 91 | yin[0] = 0 | ||
| 92 | yin[1:] = sqrsum - fftout.real[1:W] | ||
| 93 | return yin / B | ||
| 94 | |||
| 95 | def cumdiff(yin): | ||
| 96 | """ compute the cumulative mean normalized difference """ | ||
| 97 | W = len(yin) | ||
| 98 | yin[0] = 1. | ||
| 99 | cumsum = 0. | ||
| 100 | for tau in range(1, W): | ||
| 101 | cumsum += yin[tau] | ||
| 102 | if cumsum != 0: | ||
| 103 | yin[tau] *= tau/cumsum | ||
| 104 | else: | ||
| 105 | yin[tau] = 1 | ||
| 106 | return yin | ||
| 107 | |||
| 108 | def compute_all(x): | ||
| 109 | import time | ||
| 110 | now = time.time() | ||
| 111 | |||
| 112 | yin = sqd_yin(x) | ||
| 113 | t1 = time.time() | ||
| 114 | print ("yin took %.2fms" % ((t1-now) * 1000.)) | ||
| 115 | |||
| 116 | yinfast = sqd_yinfast(x) | ||
| 117 | t2 = time.time() | ||
| 118 | print ("yinfast took: %.2fms" % ((t2-t1) * 1000.)) | ||
| 119 | |||
| 120 | yintapered = sqd_yintapered(x) | ||
| 121 | t3 = time.time() | ||
| 122 | print ("yintapered took: %.2fms" % ((t3-t2) * 1000.)) | ||
| 123 | |||
| 124 | yinfft = sqd_yinfft(x) | ||
| 125 | t4 = time.time() | ||
| 126 | print ("yinfft took: %.2fms" % ((t4-t3) * 1000.)) | ||
| 127 | |||
| 128 | return yin, yinfast, yintapered, yinfft | ||
| 129 | |||
| 130 | def plot_all(yin, yinfast, yintapered, yinfft): | ||
| 131 | fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey='col') | ||
| 132 | |||
| 133 | axes[0, 0].plot(yin, label='yin') | ||
| 134 | axes[0, 0].plot(yintapered, label='yintapered') | ||
| 135 | axes[0, 0].set_ylim(bottom=0) | ||
| 136 | axes[0, 0].legend() | ||
| 137 | axes[1, 0].plot(yinfast, '-', label='yinfast') | ||
| 138 | axes[1, 0].plot(yinfft, label='yinfft') | ||
| 139 | axes[1, 0].legend() | ||
| 140 | |||
| 141 | axes[0, 1].plot(cumdiff(yin), label='yin') | ||
| 142 | axes[0, 1].plot(cumdiff(yintapered), label='yin tapered') | ||
| 143 | axes[0, 1].set_ylim(bottom=0) | ||
| 144 | axes[0, 1].legend() | ||
| 145 | axes[1, 1].plot(cumdiff(yinfast), '-', label='yinfast') | ||
| 146 | axes[1, 1].plot(cumdiff(yinfft), label='yinfft') | ||
| 147 | axes[1, 1].legend() | ||
| 148 | |||
| 149 | fig.tight_layout() | ||
| 150 | |||
| 151 | testfreqs = [441., 800., 10000., 40.] | ||
| 152 | |||
| 153 | if len(sys.argv) > 1: | ||
| 154 | testfreqs = map(float,sys.argv[1:]) | ||
| 155 | |||
| 156 | for f in testfreqs: | ||
| 157 | print ("Comparing yin implementations for sine wave at %.fHz" % f) | ||
| 158 | samplerate = 44100. | ||
| 159 | win_s = 4096 | ||
| 160 | |||
| 161 | x = np.cos(2.*np.pi * np.arange(win_s) * f / samplerate) | ||
| 162 | |||
| 163 | n_times = 1#00 | ||
| 164 | for n in range(n_times): | ||
| 165 | yin, yinfast, yinfftslow, yinfft = compute_all(x) | ||
| 166 | if 0: # plot difference | ||
| 167 | plt.plot(yin-yinfast) | ||
| 168 | plt.tight_layout() | ||
| 169 | plt.show() | ||
| 170 | if 1: | ||
| 171 | plt.plot(yinfftslow-yinfft) | ||
| 172 | plt.tight_layout() | ||
| 173 | plt.show() | ||
| 174 | plot_all(yin, yinfast, yinfftslow, yinfft) | ||
| 175 | plt.show() | ||
| @@ -27,7 +27,7 @@ · 26 unmodified lines | |||
| 27 | #ifdef USE_LOCAL_AUBIO | 27 | #ifdef USE_LOCAL_AUBIO |
| 28 | #include "aubio.h" | 28 | #include "aubio.h" |
| 29 | #else | 29 | #else |
| 30 | #include "aubio/aubio.h" | 30 | #include <aubio/aubio.h> |
| 31 | #endif | 31 | #endif |
| 32 | 32 | ||
| 33 | #define Py_default_vector_length 1024 | 33 | #define Py_default_vector_length 1024 |
| @@ -46,6 +46,14 @@ · 12 unmodified lines | |||
| 46 | #define AUBIO_NPY_SMPL_CHR "f" | 46 | #define AUBIO_NPY_SMPL_CHR "f" |
| 47 | #endif | 47 | #endif |
| 48 | 48 | ||
| 49 | #ifndef PATH_MAX | ||
| 50 | #ifdef MAX_PATH | ||
| 51 | #define PATH_MAX MAX_PATH | ||
| 52 | #else | ||
| 53 | #define PATH_MAX 1024 | ||
| 54 | #endif | ||
| 55 | #endif | ||
| 56 | |||
| 49 | // compat with Python < 2.6 | 57 | // compat with Python < 2.6 |
| 50 | #ifndef Py_TYPE | 58 | #ifndef Py_TYPE |
| 51 | #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) | 59 | #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) |
| @@ -2,77 +2,202 @@ · 1 unmodified line | |||
| 2 | #include "aubio-types.h" | 2 | #include "aubio-types.h" |
| 3 | #include "py-musicutils.h" | 3 | #include "py-musicutils.h" |
| 4 | 4 | ||
| 5 | // this dummy macro is used to convince windows that a string passed as -D flag | ||
| 6 | // is just that, a string, and not a double. | ||
| 7 | #define REDEFINESTRING(x) #x | ||
| 8 | #define DEFINEDSTRING(x) REDEFINESTRING(x) | ||
| 9 | |||
| 5 | static char aubio_module_doc[] = "Python module for the aubio library"; | 10 | static char aubio_module_doc[] = "Python module for the aubio library"; |
| 6 | 11 | ||
| 7 | static char Py_alpha_norm_doc[] = "" | 12 | static char Py_alpha_norm_doc[] = "" |
| 8 | "alpha_norm(fvec, integer) -> float\n" | 13 | "alpha_norm(vec, alpha)\n" |
| 14 | "\n" | ||
| 15 | "Compute `alpha` normalisation factor of vector `vec`.\n" | ||
| 9 | "\n" | 16 | "\n" |
| 10 | "Compute alpha normalisation factor on vector, given alpha\n" | 17 | "Parameters\n" |
| 18 | "----------\n" | ||
| 19 | "vec : fvec\n" | ||
| 20 | " input vector\n" | ||
| 21 | "alpha : float\n" | ||
| 22 | " norm factor\n" | ||
| 23 | "\n" | ||
| 24 | "Returns\n" | ||
| 25 | "-------\n" | ||
| 26 | "float\n" | ||
| 27 | " p-norm of the input vector, where `p=alpha`\n" | ||
| 11 | "\n" | 28 | "\n" |
| 12 | "Example\n" | 29 | "Example\n" |
| 13 | "-------\n" | 30 | "-------\n" |
| 14 | "\n" | 31 | "\n" |
| 15 | ">>> b = alpha_norm(a, 9)"; | 32 | ">>> a = aubio.fvec(np.arange(10)); alpha = 2\n" |
| 33 | ">>> aubio.alpha_norm(a, alpha), (sum(a**alpha)/len(a))**(1./alpha)\n" | ||
| 34 | "(5.338539123535156, 5.338539126015656)\n" | ||
| 35 | "\n" | ||
| 36 | "Note\n" | ||
| 37 | "----\n" | ||
| 38 | "Computed as:\n" | ||
| 39 | "\n" | ||
| 40 | ".. math::\n" | ||
| 41 | " l_{\\alpha} = \n" | ||
| 42 | " \\|\\frac{\\sum_{n=0}^{N-1}{{x_n}^{\\alpha}}}{N}\\|^{1/\\alpha}\n" | ||
| 43 | ""; | ||
| 16 | 44 | ||
| 17 | static char Py_bintomidi_doc[] = "" | 45 | static char Py_bintomidi_doc[] = "" |
| 18 | "bintomidi(float, samplerate = integer, fftsize = integer) -> float\n" | 46 | "bintomidi(fftbin, samplerate, fftsize)\n" |
| 47 | "\n" | ||
| 48 | "Convert FFT bin to frequency in midi note, given the sampling rate\n" | ||
| 49 | "and the size of the FFT.\n" | ||
| 19 | "\n" | 50 | "\n" |
| 20 | "Convert bin (float) to midi (float), given the sampling rate and the FFT size\n" | 51 | "Parameters\n" |
| 52 | "----------\n" | ||
| 53 | "fftbin : float\n" | ||
| 54 | " input frequency bin\n" | ||
| 55 | "samplerate : float\n" | ||
| 56 | " sampling rate of the signal\n" | ||
| 57 | "fftsize : float\n" | ||
| 58 | " size of the FFT\n" | ||
| 59 | "\n" | ||
| 60 | "Returns\n" | ||
| 61 | "-------\n" | ||
| 62 | "float\n" | ||
| 63 | " Frequency converted to midi note.\n" | ||
| 21 | "\n" | 64 | "\n" |
| 22 | "Example\n" | 65 | "Example\n" |
| 23 | "-------\n" | 66 | "-------\n" |
| 24 | "\n" | 67 | "\n" |
| 25 | ">>> midi = bintomidi(float, samplerate = 44100, fftsize = 1024)"; | 68 | ">>> aubio.bintomidi(10, 44100, 1024)\n" |
| 69 | "68.62871551513672\n" | ||
| 70 | ""; | ||
| 26 | 71 | ||
| 27 | static char Py_miditobin_doc[] = "" | 72 | static char Py_miditobin_doc[] = "" |
| 28 | "miditobin(float, samplerate = integer, fftsize = integer) -> float\n" | 73 | "miditobin(midi, samplerate, fftsize)\n" |
| 29 | "\n" | 74 | "\n" |
| 30 | "Convert midi (float) to bin (float), given the sampling rate and the FFT size\n" | 75 | "Convert frequency in midi note to FFT bin, given the sampling rate\n" |
| 76 | "and the size of the FFT.\n" | ||
| 31 | "\n" | 77 | "\n" |
| 32 | "Example\n" | 78 | "Parameters\n" |
| 79 | "----------\n" | ||
| 80 | "midi : float\n" | ||
| 81 | " input frequency, in midi note\n" | ||
| 82 | "samplerate : float\n" | ||
| 83 | " sampling rate of the signal\n" | ||
| 84 | "fftsize : float\n" | ||
| 85 | " size of the FFT\n" | ||
| 86 | "\n" | ||
| 87 | "Returns\n" | ||
| 33 | "-------\n" | 88 | "-------\n" |
| 89 | "float\n" | ||
| 90 | " Frequency converted to FFT bin.\n" | ||
| 91 | "\n" | ||
| 92 | "Examples\n" | ||
| 93 | "--------\n" | ||
| 34 | "\n" | 94 | "\n" |
| 35 | ">>> bin = miditobin(midi, samplerate = 44100, fftsize = 1024)"; | 95 | ">>> aubio.miditobin(69, 44100, 1024)\n" |
| 96 | "10.216779708862305\n" | ||
| 97 | ">>> aubio.miditobin(75.08, 32000, 512)\n" | ||
| 98 | "10.002175331115723\n" | ||
| 99 | ""; | ||
| 36 | 100 | ||
| 37 | static char Py_bintofreq_doc[] = "" | 101 | static char Py_bintofreq_doc[] = "" |
| 38 | "bintofreq(float, samplerate = integer, fftsize = integer) -> float\n" | 102 | "bintofreq(fftbin, samplerate, fftsize)\n" |
| 103 | "\n" | ||
| 104 | "Convert FFT bin to frequency in Hz, given the sampling rate\n" | ||
| 105 | "and the size of the FFT.\n" | ||
| 39 | "\n" | 106 | "\n" |
| 40 | "Convert bin number (float) in frequency (Hz), given the sampling rate and the FFT size\n" | 107 | "Parameters\n" |
| 108 | "----------\n" | ||
| 109 | "fftbin : float\n" | ||
| 110 | " input frequency bin\n" | ||
| 111 | "samplerate : float\n" | ||
| 112 | " sampling rate of the signal\n" | ||
| 113 | "fftsize : float\n" | ||
| 114 | " size of the FFT\n" | ||
| 115 | "\n" | ||
| 116 | "Returns\n" | ||
| 117 | "-------\n" | ||
| 118 | "float\n" | ||
| 119 | " Frequency converted to Hz.\n" | ||
| 41 | "\n" | 120 | "\n" |
| 42 | "Example\n" | 121 | "Example\n" |
| 43 | "-------\n" | 122 | "-------\n" |
| 44 | "\n" | 123 | "\n" |
| 45 | ">>> freq = bintofreq(bin, samplerate = 44100, fftsize = 1024)"; | 124 | ">>> aubio.bintofreq(10, 44100, 1024)\n" |
| 125 | "430.6640625\n" | ||
| 126 | ""; | ||
| 46 | 127 | ||
| 47 | static char Py_freqtobin_doc[] = "" | 128 | static char Py_freqtobin_doc[] = "" |
| 48 | "freqtobin(float, samplerate = integer, fftsize = integer) -> float\n" | 129 | "freqtobin(freq, samplerate, fftsize)\n" |
| 49 | "\n" | 130 | "\n" |
| 50 | "Convert frequency (Hz) in bin number (float), given the sampling rate and the FFT size\n" | 131 | "Convert frequency in Hz to FFT bin, given the sampling rate\n" |
| 132 | "and the size of the FFT.\n" | ||
| 51 | "\n" | 133 | "\n" |
| 52 | "Example\n" | 134 | "Parameters\n" |
| 135 | "----------\n" | ||
| 136 | "midi : float\n" | ||
| 137 | " input frequency, in midi note\n" | ||
| 138 | "samplerate : float\n" | ||
| 139 | " sampling rate of the signal\n" | ||
| 140 | "fftsize : float\n" | ||
| 141 | " size of the FFT\n" | ||
| 142 | "\n" | ||
| 143 | "Returns\n" | ||
| 53 | "-------\n" | 144 | "-------\n" |
| 145 | "float\n" | ||
| 146 | " Frequency converted to FFT bin.\n" | ||
| 147 | "\n" | ||
| 148 | "Examples\n" | ||
| 149 | "--------\n" | ||
| 54 | "\n" | 150 | "\n" |
| 55 | ">>> bin = freqtobin(freq, samplerate = 44100, fftsize = 1024)"; | 151 | ">>> aubio.freqtobin(440, 44100, 1024)\n" |
| 152 | "10.216779708862305\n" | ||
| 153 | ""; | ||
| 56 | 154 | ||
| 57 | static char Py_zero_crossing_rate_doc[] = "" | 155 | static char Py_zero_crossing_rate_doc[] = "" |
| 58 | "zero_crossing_rate(fvec) -> float\n" | 156 | "zero_crossing_rate(vec)\n" |
| 157 | "\n" | ||
| 158 | "Compute zero-crossing rate of `vec`.\n" | ||
| 59 | "\n" | 159 | "\n" |
| 60 | "Compute Zero crossing rate of a vector\n" | 160 | "Parameters\n" |
| 161 | "----------\n" | ||
| 162 | "vec : fvec\n" | ||
| 163 | " input vector\n" | ||
| 164 | "\n" | ||
| 165 | "Returns\n" | ||
| 166 | "-------\n" | ||
| 167 | "float\n" | ||
| 168 | " Zero-crossing rate.\n" | ||
| 61 | "\n" | 169 | "\n" |
| 62 | "Example\n" | 170 | "Example\n" |
| 63 | "-------\n" | 171 | "-------\n" |
| 64 | "\n" | 172 | "\n" |
| 65 | ">>> z = zero_crossing_rate(a)"; | 173 | ">>> a = np.linspace(-1., 1., 1000, dtype=aubio.float_type)\n" |
| 174 | ">>> aubio.zero_crossing_rate(a), 1/1000\n" | ||
| 175 | "(0.0010000000474974513, 0.001)\n" | ||
| 176 | ""; | ||
| 66 | 177 | ||
| 67 | static char Py_min_removal_doc[] = "" | 178 | static char Py_min_removal_doc[] = "" |
| 68 | "min_removal(fvec) -> float\n" | 179 | "min_removal(vec)\n" |
| 180 | "\n" | ||
| 181 | "Remove the minimum value of a vector to each of its element.\n" | ||
| 69 | "\n" | 182 | "\n" |
| 70 | "Remove the minimum value of a vector, in-place modification\n" | 183 | "Modifies the input vector in-place and returns a reference to it.\n" |
| 184 | "\n" | ||
| 185 | "Parameters\n" | ||
| 186 | "----------\n" | ||
| 187 | "vec : fvec\n" | ||
| 188 | " input vector\n" | ||
| 189 | "\n" | ||
| 190 | "Returns\n" | ||
| 191 | "-------\n" | ||
| 192 | "fvec\n" | ||
| 193 | " modified input vector\n" | ||
| 71 | "\n" | 194 | "\n" |
| 72 | "Example\n" | 195 | "Example\n" |
| 73 | "-------\n" | 196 | "-------\n" |
| 74 | "\n" | 197 | "\n" |
| 75 | ">>> min_removal(a)"; | 198 | ">>> aubio.min_removal(aubio.fvec(np.arange(1,4)))\n" |
| 199 | "array([0., 1., 2.], dtype=" AUBIO_NPY_SMPL_STR ")\n" | ||
| 200 | ""; | ||
| 76 | 201 | ||
| 77 | extern void add_ufuncs ( PyObject *m ); | 202 | extern void add_ufuncs ( PyObject *m ); |
| 78 | extern int generated_types_ready(void); | 203 | extern int generated_types_ready(void); |
| @@ -112,7 +237,9 @@ Py_bintomidi (PyObject * self, PyObject * args) · 33 unmodified lines | |||
| 112 | smpl_t input, samplerate, fftsize; | 237 | smpl_t input, samplerate, fftsize; |
| 113 | smpl_t output; | 238 | smpl_t output; |
| 114 | 239 | ||
| 115 | if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) { | 240 | if (!PyArg_ParseTuple (args, |
| 241 | "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, | ||
| 242 | &input, &samplerate, &fftsize)) { | ||
| 116 | return NULL; | 243 | return NULL; |
| 117 | } | 244 | } |
| 118 | 245 | ||
| @@ -127,7 +254,9 @@ Py_miditobin (PyObject * self, PyObject * args) · 8 unmodified lines | |||
| 127 | smpl_t input, samplerate, fftsize; | 254 | smpl_t input, samplerate, fftsize; |
| 128 | smpl_t output; | 255 | smpl_t output; |
| 129 | 256 | ||
| 130 | if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) { | 257 | if (!PyArg_ParseTuple (args, |
| 258 | "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, | ||
| 259 | &input, &samplerate, &fftsize)) { | ||
| 131 | return NULL; | 260 | return NULL; |
| 132 | } | 261 | } |
| 133 | 262 | ||
| @@ -142,7 +271,9 @@ Py_bintofreq (PyObject * self, PyObject * args) · 8 unmodified lines | |||
| 142 | smpl_t input, samplerate, fftsize; | 271 | smpl_t input, samplerate, fftsize; |
| 143 | smpl_t output; | 272 | smpl_t output; |
| 144 | 273 | ||
| 145 | if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) { | 274 | if (!PyArg_ParseTuple (args, |
| 275 | "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, | ||
| 276 | &input, &samplerate, &fftsize)) { | ||
| 146 | return NULL; | 277 | return NULL; |
| 147 | } | 278 | } |
| 148 | 279 | ||
| @@ -157,7 +288,9 @@ Py_freqtobin (PyObject * self, PyObject * args) · 8 unmodified lines | |||
| 157 | smpl_t input, samplerate, fftsize; | 288 | smpl_t input, samplerate, fftsize; |
| 158 | smpl_t output; | 289 | smpl_t output; |
| 159 | 290 | ||
| 160 | if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) { | 291 | if (!PyArg_ParseTuple (args, |
| 292 | "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, | ||
| 293 | &input, &samplerate, &fftsize)) { | ||
| 161 | return NULL; | 294 | return NULL; |
| 162 | } | 295 | } |
| 163 | 296 | ||
| @@ -237,6 +370,12 @@ static PyMethodDef aubio_methods[] = { · 73 unmodified lines | |||
| 237 | {"silence_detection", Py_aubio_silence_detection, METH_VARARGS, Py_aubio_silence_detection_doc}, | 370 | {"silence_detection", Py_aubio_silence_detection, METH_VARARGS, Py_aubio_silence_detection_doc}, |
| 238 | {"level_detection", Py_aubio_level_detection, METH_VARARGS, Py_aubio_level_detection_doc}, | 371 | {"level_detection", Py_aubio_level_detection, METH_VARARGS, Py_aubio_level_detection_doc}, |
| 239 | {"window", Py_aubio_window, METH_VARARGS, Py_aubio_window_doc}, | 372 | {"window", Py_aubio_window, METH_VARARGS, Py_aubio_window_doc}, |
| 373 | {"shift", Py_aubio_shift, METH_VARARGS, Py_aubio_shift_doc}, | ||
| 374 | {"ishift", Py_aubio_ishift, METH_VARARGS, Py_aubio_ishift_doc}, | ||
| 375 | {"hztomel", Py_aubio_hztomel, METH_VARARGS|METH_KEYWORDS, Py_aubio_hztomel_doc}, | ||
| 376 | {"meltohz", Py_aubio_meltohz, METH_VARARGS|METH_KEYWORDS, Py_aubio_meltohz_doc}, | ||
| 377 | {"hztomel_htk", Py_aubio_hztomel_htk, METH_VARARGS, Py_aubio_hztomel_htk_doc}, | ||
| 378 | {"meltohz_htk", Py_aubio_meltohz_htk, METH_VARARGS, Py_aubio_meltohz_htk_doc}, | ||
| 240 | {NULL, NULL, 0, NULL} /* Sentinel */ | 379 | {NULL, NULL, 0, NULL} /* Sentinel */ |
| 241 | }; | 380 | }; |
| 242 | 381 | ||
| @@ -323,6 +462,7 @@ initaubio (void) · 80 unmodified lines | |||
| 323 | PyModule_AddObject (m, "sink", (PyObject *) & Py_sinkType); | 462 | PyModule_AddObject (m, "sink", (PyObject *) & Py_sinkType); |
| 324 | 463 | ||
| 325 | PyModule_AddStringConstant(m, "float_type", AUBIO_NPY_SMPL_STR); | 464 | PyModule_AddStringConstant(m, "float_type", AUBIO_NPY_SMPL_STR); |
| 465 | PyModule_AddStringConstant(m, "__version__", DEFINEDSTRING(AUBIO_VERSION)); | ||
| 326 | 466 | ||
| 327 | // add generated objects | 467 | // add generated objects |
| 328 | add_generated_objects(m); | 468 | add_generated_objects(m); |
| @@ -19,7 +19,37 @@ typedef struct · 18 unmodified lines | |||
| 19 | uint_t length; | 19 | uint_t length; |
| 20 | } Py_cvec; | 20 | } Py_cvec; |
| 21 | 21 | ||
| 22 | static char Py_cvec_doc[] = "cvec object"; | 22 | static char Py_cvec_doc[] = "" |
| 23 | "cvec(size)\n" | ||
| 24 | "\n" | ||
| 25 | "A container holding spectral data.\n" | ||
| 26 | "\n" | ||
| 27 | "Create one `cvec` to store the spectral information of a window\n" | ||
| 28 | "of `size` points. The data will be stored in two vectors,\n" | ||
| 29 | ":attr:`phas` and :attr:`norm`, each of shape (:attr:`length`,),\n" | ||
| 30 | "with `length = size // 2 + 1`.\n" | ||
| 31 | "\n" | ||
| 32 | "Parameters\n" | ||
| 33 | "----------\n" | ||
| 34 | "size: int\n" | ||
| 35 | " Size of spectrum to create.\n" | ||
| 36 | "\n" | ||
| 37 | "Examples\n" | ||
| 38 | "--------\n" | ||
| 39 | ">>> c = aubio.cvec(1024)\n" | ||
| 40 | ">>> c\n" | ||
| 41 | "aubio cvec of 513 elements\n" | ||
| 42 | ">>> c.length\n" | ||
| 43 | "513\n" | ||
| 44 | ">>> c.norm.dtype, c.phas.dtype\n" | ||
| 45 | "(dtype('float32'), dtype('float32'))\n" | ||
| 46 | ">>> c.norm.shape, c.phas.shape\n" | ||
| 47 | "((513,), (513,))\n" | ||
| 48 | "\n" | ||
| 49 | "See Also\n" | ||
| 50 | "--------\n" | ||
| 51 | "fvec, fft, pvoc\n" | ||
| 52 | ""; | ||
| 23 | 53 | ||
| 24 | 54 | ||
| 25 | PyObject * | 55 | PyObject * |
| @@ -142,14 +172,14 @@ Py_cvec_set_norm (Py_cvec * vec, PyObject *input, void * closure) · 116 unmodified lines | |||
| 142 | { | 172 | { |
| 143 | npy_intp length; | 173 | npy_intp length; |
| 144 | if (!PyAubio_IsValidVector(input)) { | 174 | if (!PyAubio_IsValidVector(input)) { |
| 145 | return 1; | 175 | return -1; |
| 146 | } | 176 | } |
| 147 | length = PyArray_SIZE ((PyArrayObject *)input); | 177 | length = PyArray_SIZE ((PyArrayObject *)input); |
| 148 | if (length != vec->length) { | 178 | if (length != vec->length) { |
| 149 | PyErr_Format (PyExc_ValueError, | 179 | PyErr_Format (PyExc_ValueError, |
| 150 | "input array has length %ld, but cvec has length %d", length, | 180 | "input array has length %" NPY_INTP_FMT ", but cvec has length %d", length, |
| 151 | vec->length); | 181 | vec->length); |
| 152 | return 1; | 182 | return -1; |
| 153 | } | 183 | } |
| 154 | 184 | ||
| 155 | Py_XDECREF(vec->norm); | 185 | Py_XDECREF(vec->norm); |
| @@ -163,14 +193,14 @@ Py_cvec_set_phas (Py_cvec * vec, PyObject *input, void * closure) · 7 unmodified lines | |||
| 163 | { | 193 | { |
| 164 | npy_intp length; | 194 | npy_intp length; |
| 165 | if (!PyAubio_IsValidVector(input)) { | 195 | if (!PyAubio_IsValidVector(input)) { |
| 166 | return 1; | 196 | return -1; |
| 167 | } | 197 | } |
| 168 | length = PyArray_SIZE ((PyArrayObject *)input); | 198 | length = PyArray_SIZE ((PyArrayObject *)input); |
| 169 | if (length != vec->length) { | 199 | if (length != vec->length) { |
| 170 | PyErr_Format (PyExc_ValueError, | 200 | PyErr_Format (PyExc_ValueError, |
| 171 | "input array has length %ld, but cvec has length %d", length, | 201 | "input array has length %" NPY_INTP_FMT ", but cvec has length %d", length, |
| 172 | vec->length); | 202 | vec->length); |
| 173 | return 1; | 203 | return -1; |
| 174 | } | 204 | } |
| 175 | 205 | ||
| 176 | Py_XDECREF(vec->phas); | 206 | Py_XDECREF(vec->phas); |
| @@ -182,7 +212,7 @@ Py_cvec_set_phas (Py_cvec * vec, PyObject *input, void * closure) · 5 unmodified lines | |||
| 182 | static PyMemberDef Py_cvec_members[] = { | 212 | static PyMemberDef Py_cvec_members[] = { |
| 183 | // TODO remove READONLY flag and define getter/setter | 213 | // TODO remove READONLY flag and define getter/setter |
| 184 | {"length", T_INT, offsetof (Py_cvec, length), READONLY, | 214 | {"length", T_INT, offsetof (Py_cvec, length), READONLY, |
| 185 | "length attribute"}, | 215 | "int: Length of `norm` and `phas` vectors."}, |
| 186 | {NULL} /* Sentinel */ | 216 | {NULL} /* Sentinel */ |
| 187 | }; | 217 | }; |
| 188 | 218 | ||
| @@ -191,11 +221,11 @@ static PyMethodDef Py_cvec_methods[] = { · 2 unmodified lines | |||
| 191 | }; | 221 | }; |
| 192 | 222 | ||
| 193 | static PyGetSetDef Py_cvec_getseters[] = { | 223 | static PyGetSetDef Py_cvec_getseters[] = { |
| 194 | {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm, | 224 | {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm, |
| 195 | "Numpy vector of shape (length,) containing the magnitude", | 225 | "numpy.ndarray: Vector of shape `(length,)` containing the magnitude.", |
| 196 | NULL}, | 226 | NULL}, |
| 197 | {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas, | 227 | {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas, |
| 198 | "Numpy vector of shape (length,) containing the phase", | 228 | "numpy.ndarray: Vector of shape `(length,)` containing the phase.", |
| 199 | NULL}, | 229 | NULL}, |
| 200 | {NULL} /* sentinel */ | 230 | {NULL} /* sentinel */ |
| 201 | }; | 231 | }; |
| @@ -94,7 +94,7 @@ Py_filterbank_do(Py_filterbank * self, PyObject * args) · 93 unmodified lines | |||
| 94 | 94 | ||
| 95 | if (self->vec.length != self->win_s / 2 + 1) { | 95 | if (self->vec.length != self->win_s / 2 + 1) { |
| 96 | PyErr_Format(PyExc_ValueError, | 96 | PyErr_Format(PyExc_ValueError, |
| 97 | "input cvec has length %d, but fft expects length %d", | 97 | "input cvec has length %d, but filterbank expects length %d", |
| 98 | self->vec.length, self->win_s / 2 + 1); | 98 | self->vec.length, self->win_s / 2 + 1); |
| 99 | return NULL; | 99 | return NULL; |
| 100 | } | 100 | } |
| @@ -122,8 +122,8 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) · 21 unmodified lines | |||
| 122 | uint_t err = 0; | 122 | uint_t err = 0; |
| 123 | 123 | ||
| 124 | PyObject *input; | 124 | PyObject *input; |
| 125 | uint_t samplerate; | 125 | smpl_t samplerate; |
| 126 | if (!PyArg_ParseTuple (args, "OI", &input, &samplerate)) { | 126 | if (!PyArg_ParseTuple (args, "O" AUBIO_NPY_SMPL_CHR, &input, &samplerate)) { |
| 127 | return NULL; | 127 | return NULL; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| @@ -138,8 +138,14 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) · 8 unmodified lines | |||
| 138 | err = aubio_filterbank_set_triangle_bands (self->o, | 138 | err = aubio_filterbank_set_triangle_bands (self->o, |
| 139 | &(self->freqs), samplerate); | 139 | &(self->freqs), samplerate); |
| 140 | if (err > 0) { | 140 | if (err > 0) { |
| 141 | PyErr_SetString (PyExc_ValueError, | 141 | if (PyErr_Occurred() == NULL) { |
| 142 | "error when setting filter to A-weighting"); | 142 | PyErr_SetString (PyExc_ValueError, "error running set_triangle_bands"); |
| 143 | } else { | ||
| 144 | // change the RuntimeError into ValueError | ||
| 145 | PyObject *type, *value, *traceback; | ||
| 146 | PyErr_Fetch(&type, &value, &traceback); | ||
| 147 | PyErr_Restore(PyExc_ValueError, value, traceback); | ||
| 148 | } | ||
| 143 | return NULL; | 149 | return NULL; |
| 144 | } | 150 | } |
| 145 | Py_RETURN_NONE; | 151 | Py_RETURN_NONE; |
| @@ -150,15 +156,79 @@ Py_filterbank_set_mel_coeffs_slaney (Py_filterbank * self, PyObject *args) · 4 unmodified lines | |||
| 150 | { | 156 | { |
| 151 | uint_t err = 0; | 157 | uint_t err = 0; |
| 152 | 158 | ||
| 153 | uint_t samplerate; | 159 | smpl_t samplerate; |
| 154 | if (!PyArg_ParseTuple (args, "I", &samplerate)) { | 160 | if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &samplerate)) { |
| 155 | return NULL; | 161 | return NULL; |
| 156 | } | 162 | } |
| 157 | 163 | ||
| 158 | err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate); | 164 | err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate); |
| 159 | if (err > 0) { | 165 | if (err > 0) { |
| 160 | PyErr_SetString (PyExc_ValueError, | 166 | if (PyErr_Occurred() == NULL) { |
| 161 | "error when setting filter to A-weighting"); | 167 | PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_slaney"); |
| 168 | } else { | ||
| 169 | // change the RuntimeError into ValueError | ||
| 170 | PyObject *type, *value, *traceback; | ||
| 171 | PyErr_Fetch(&type, &value, &traceback); | ||
| 172 | PyErr_Restore(PyExc_ValueError, value, traceback); | ||
| 173 | } | ||
| 174 | return NULL; | ||
| 175 | } | ||
| 176 | Py_RETURN_NONE; | ||
| 177 | } | ||
| 178 | |||
| 179 | static PyObject * | ||
| 180 | Py_filterbank_set_mel_coeffs (Py_filterbank * self, PyObject *args) | ||
| 181 | { | ||
| 182 | uint_t err = 0; | ||
| 183 | |||
| 184 | smpl_t samplerate; | ||
| 185 | smpl_t freq_min; | ||
| 186 | smpl_t freq_max; | ||
| 187 | if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR | ||
| 188 | AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) { | ||
| 189 | return NULL; | ||
| 190 | } | ||
| 191 | |||
| 192 | err = aubio_filterbank_set_mel_coeffs (self->o, samplerate, | ||
| 193 | freq_min, freq_max); | ||
| 194 | if (err > 0) { | ||
| 195 | if (PyErr_Occurred() == NULL) { | ||
| 196 | PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs"); | ||
| 197 | } else { | ||
| 198 | // change the RuntimeError into ValueError | ||
| 199 | PyObject *type, *value, *traceback; | ||
| 200 | PyErr_Fetch(&type, &value, &traceback); | ||
| 201 | PyErr_Restore(PyExc_ValueError, value, traceback); | ||
| 202 | } | ||
| 203 | return NULL; | ||
| 204 | } | ||
| 205 | Py_RETURN_NONE; | ||
| 206 | } | ||
| 207 | |||
| 208 | static PyObject * | ||
| 209 | Py_filterbank_set_mel_coeffs_htk (Py_filterbank * self, PyObject *args) | ||
| 210 | { | ||
| 211 | uint_t err = 0; | ||
| 212 | |||
| 213 | smpl_t samplerate; | ||
| 214 | smpl_t freq_min; | ||
| 215 | smpl_t freq_max; | ||
| 216 | if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR | ||
| 217 | AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) { | ||
| 218 | return NULL; | ||
| 219 | } | ||
| 220 | |||
| 221 | err = aubio_filterbank_set_mel_coeffs_htk (self->o, samplerate, | ||
| 222 | freq_min, freq_max); | ||
| 223 | if (err > 0) { | ||
| 224 | if (PyErr_Occurred() == NULL) { | ||
| 225 | PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_htk"); | ||
| 226 | } else { | ||
| 227 | // change the RuntimeError into ValueError | ||
| 228 | PyObject *type, *value, *traceback; | ||
| 229 | PyErr_Fetch(&type, &value, &traceback); | ||
| 230 | PyErr_Restore(PyExc_ValueError, value, traceback); | ||
| 231 | } | ||
| 162 | return NULL; | 232 | return NULL; |
| 163 | } | 233 | } |
| 164 | Py_RETURN_NONE; | 234 | Py_RETURN_NONE; |
| @@ -195,15 +265,69 @@ Py_filterbank_get_coeffs (Py_filterbank * self, PyObject *unused) · 30 unmodified lines | |||
| 195 | aubio_filterbank_get_coeffs (self->o) ); | 265 | aubio_filterbank_get_coeffs (self->o) ); |
| 196 | } | 266 | } |
| 197 | 267 | ||
| 268 | static PyObject * | ||
| 269 | Py_filterbank_set_power(Py_filterbank *self, PyObject *args) | ||
| 270 | { | ||
| 271 | smpl_t power; | ||
| 272 | |||
| 273 | if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &power)) { | ||
| 274 | return NULL; | ||
| 275 | } | ||
| 276 | if(aubio_filterbank_set_power (self->o, power)) { | ||
| 277 | if (PyErr_Occurred() == NULL) { | ||
| 278 | PyErr_SetString (PyExc_ValueError, | ||
| 279 | "error running filterbank.set_power"); | ||
| 280 | } else { | ||
| 281 | // change the RuntimeError into ValueError | ||
| 282 | PyObject *type, *value, *traceback; | ||
| 283 | PyErr_Fetch(&type, &value, &traceback); | ||
| 284 | PyErr_Restore(PyExc_ValueError, value, traceback); | ||
| 285 | } | ||
| 286 | return NULL; | ||
| 287 | } | ||
| 288 | Py_RETURN_NONE; | ||
| 289 | } | ||
| 290 | |||
| 291 | static PyObject * | ||
| 292 | Py_filterbank_set_norm(Py_filterbank *self, PyObject *args) | ||
| 293 | { | ||
| 294 | smpl_t norm; | ||
| 295 | |||
| 296 | if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &norm)) { | ||
| 297 | return NULL; | ||
| 298 | } | ||
| 299 | if(aubio_filterbank_set_norm (self->o, norm)) { | ||
| 300 | if (PyErr_Occurred() == NULL) { | ||
| 301 | PyErr_SetString (PyExc_ValueError, | ||
| 302 | "error running filterbank.set_power"); | ||
| 303 | } else { | ||
| 304 | // change the RuntimeError into ValueError | ||
| 305 | PyObject *type, *value, *traceback; | ||
| 306 | PyErr_Fetch(&type, &value, &traceback); | ||
| 307 | PyErr_Restore(PyExc_ValueError, value, traceback); | ||
| 308 | } | ||
| 309 | return NULL; | ||
| 310 | } | ||
| 311 | Py_RETURN_NONE; | ||
| 312 | } | ||
| 313 | |||
| 198 | static PyMethodDef Py_filterbank_methods[] = { | 314 | static PyMethodDef Py_filterbank_methods[] = { |
| 199 | {"set_triangle_bands", (PyCFunction) Py_filterbank_set_triangle_bands, | 315 | {"set_triangle_bands", (PyCFunction) Py_filterbank_set_triangle_bands, |
| 200 | METH_VARARGS, "set coefficients of filterbanks"}, | 316 | METH_VARARGS, "set coefficients of filterbanks"}, |
| 201 | {"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney, | 317 | {"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney, |
| 202 | METH_VARARGS, "set coefficients of filterbank as in Auditory Toolbox"}, | 318 | METH_VARARGS, "set coefficients of filterbank as in Auditory Toolbox"}, |
| 319 | {"set_mel_coeffs", (PyCFunction) Py_filterbank_set_mel_coeffs, | ||
| 320 | METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"}, | ||
| 321 | {"set_mel_coeffs_htk", (PyCFunction) Py_filterbank_set_mel_coeffs_htk, | ||
| 322 | METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"}, | ||
| 203 | {"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs, | 323 | {"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs, |
| 204 | METH_NOARGS, "get coefficients of filterbank"}, | 324 | METH_NOARGS, "get coefficients of filterbank"}, |
| 205 | {"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs, | 325 | {"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs, |
| 206 | METH_VARARGS, "set coefficients of filterbank"}, | 326 | METH_VARARGS, "set coefficients of filterbank"}, |
| 327 | {"set_power", (PyCFunction) Py_filterbank_set_power, | ||
| 328 | METH_VARARGS, "set power applied to filterbank input spectrum"}, | ||
| 329 | {"set_norm", (PyCFunction) Py_filterbank_set_norm, | ||
| 330 | METH_VARARGS, "set norm applied to filterbank input spectrum"}, | ||
| 207 | {NULL} | 331 | {NULL} |
| 208 | }; | 332 | }; |
| 209 | 333 | ||
| @@ -133,3 +133,105 @@ Py_aubio_level_detection(PyObject *self, PyObject *args) · 132 unmodified lines | |||
| 133 | 133 | ||
| 134 | return level_detection; | 134 | return level_detection; |
| 135 | } | 135 | } |
| 136 | |||
| 137 | PyObject * | ||
| 138 | Py_aubio_shift(PyObject *self, PyObject *args) | ||
| 139 | { | ||
| 140 | PyObject *input; | ||
| 141 | fvec_t vec; | ||
| 142 | |||
| 143 | if (!PyArg_ParseTuple (args, "O:shift", &input)) { | ||
| 144 | return NULL; | ||
| 145 | } | ||
| 146 | |||
| 147 | if (input == NULL) { | ||
| 148 | return NULL; | ||
| 149 | } | ||
| 150 | |||
| 151 | if (!PyAubio_ArrayToCFvec(input, &vec)) { | ||
| 152 | return NULL; | ||
| 153 | } | ||
| 154 | |||
| 155 | fvec_shift(&vec); | ||
| 156 | |||
| 157 | //Py_RETURN_NONE; | ||
| 158 | return (PyObject *) PyAubio_CFvecToArray(&vec); | ||
| 159 | } | ||
| 160 | |||
| 161 | PyObject * | ||
| 162 | Py_aubio_ishift(PyObject *self, PyObject *args) | ||
| 163 | { | ||
| 164 | PyObject *input; | ||
| 165 | fvec_t vec; | ||
| 166 | |||
| 167 | if (!PyArg_ParseTuple (args, "O:shift", &input)) { | ||
| 168 | return NULL; | ||
| 169 | } | ||
| 170 | |||
| 171 | if (input == NULL) { | ||
| 172 | return NULL; | ||
| 173 | } | ||
| 174 | |||
| 175 | if (!PyAubio_ArrayToCFvec(input, &vec)) { | ||
| 176 | return NULL; | ||
| 177 | } | ||
| 178 | |||
| 179 | fvec_ishift(&vec); | ||
| 180 | |||
| 181 | //Py_RETURN_NONE; | ||
| 182 | return (PyObject *) PyAubio_CFvecToArray(&vec); | ||
| 183 | } | ||
| 184 | |||
| 185 | PyObject* | ||
| 186 | Py_aubio_hztomel(PyObject *self, PyObject *args, PyObject *kwds) | ||
| 187 | { | ||
| 188 | smpl_t v; | ||
| 189 | PyObject *htk = NULL; | ||
| 190 | static char *kwlist[] = {"f", "htk", NULL}; | ||
| 191 | if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O", | ||
| 192 | kwlist, &v, &htk)) | ||
| 193 | { | ||
| 194 | return NULL; | ||
| 195 | } | ||
| 196 | if (htk != NULL && PyObject_IsTrue(htk) == 1) | ||
| 197 | return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel_htk(v)); | ||
| 198 | else | ||
| 199 | return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel(v)); | ||
| 200 | } | ||
| 201 | |||
| 202 | PyObject* | ||
| 203 | Py_aubio_meltohz(PyObject *self, PyObject *args, PyObject *kwds) | ||
| 204 | { | ||
| 205 | smpl_t v; | ||
| 206 | PyObject *htk = NULL; | ||
| 207 | static char *kwlist[] = {"m", "htk", NULL}; | ||
| 208 | if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O", | ||
| 209 | kwlist, &v, &htk)) | ||
| 210 | { | ||
| 211 | return NULL; | ||
| 212 | } | ||
| 213 | if (htk != NULL && PyObject_IsTrue(htk) == 1) | ||
| 214 | return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz_htk(v)); | ||
| 215 | else | ||
| 216 | return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz(v)); | ||
| 217 | } | ||
| 218 | |||
| 219 | PyObject* | ||
| 220 | Py_aubio_hztomel_htk(PyObject *self, PyObject *args) | ||
| 221 | { | ||
| 222 | smpl_t v; | ||
| 223 | if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) { | ||
| 224 | return NULL; | ||
| 225 | } | ||
| 226 | return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel_htk(v)); | ||
| 227 | } | ||
| 228 | |||
| 229 | PyObject* | ||
| 230 | Py_aubio_meltohz_htk(PyObject *self, PyObject *args) | ||
| 231 | { | ||
| 232 | smpl_t v; | ||
| 233 | if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) { | ||
| 234 | return NULL; | ||
| 235 | } | ||
| 236 | return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz_htk(v)); | ||
| 237 | } | ||
| @@ -2,73 +2,370 @@ · 1 unmodified line | |||
| 2 | #define PY_AUBIO_MUSICUTILS_H | 2 | #define PY_AUBIO_MUSICUTILS_H |
| 3 | 3 | ||
| 4 | static char Py_aubio_window_doc[] = "" | 4 | static char Py_aubio_window_doc[] = "" |
| 5 | "window(string, integer) -> fvec\n" | 5 | "window(window_type, size)\n" |
| 6 | "\n" | 6 | "\n" |
| 7 | "Create a window\n" | 7 | "Create a window of length `size`. `window_type` should be one\n" |
| 8 | "of the following:\n" | ||
| 8 | "\n" | 9 | "\n" |
| 9 | "Example\n" | 10 | "- `default` (same as `hanningz`).\n" |
| 11 | "- `ones`\n" | ||
| 12 | "- `rectangle`\n" | ||
| 13 | "- `hamming`\n" | ||
| 14 | "- `hanning`\n" | ||
| 15 | "- `hanningz` [1]_\n" | ||
| 16 | "- `blackman`\n" | ||
| 17 | "- `blackman_harris`\n" | ||
| 18 | "- `gaussian`\n" | ||
| 19 | "- `welch`\n" | ||
| 20 | "- `parzen`\n" | ||
| 21 | "\n" | ||
| 22 | "Parameters\n" | ||
| 23 | "----------\n" | ||
| 24 | "window_type : str\n" | ||
| 25 | " Type of window.\n" | ||
| 26 | "size : int\n" | ||
| 27 | " Length of window.\n" | ||
| 28 | "\n" | ||
| 29 | "Returns\n" | ||
| 10 | "-------\n" | 30 | "-------\n" |
| 31 | "fvec\n" | ||
| 32 | " Array of shape `(length,)` containing the new window.\n" | ||
| 33 | "\n" | ||
| 34 | "See Also\n" | ||
| 35 | "--------\n" | ||
| 36 | "pvoc, fft\n" | ||
| 37 | "\n" | ||
| 38 | "Examples\n" | ||
| 39 | "--------\n" | ||
| 40 | "Compute a zero-phase Hann window on `1024` points:\n" | ||
| 11 | "\n" | 41 | "\n" |
| 12 | ">>> window('hanningz', 1024)\n" | 42 | ">>> aubio.window('hanningz', 1024)\n" |
| 13 | "array([ 0.00000000e+00, 9.41753387e-06, 3.76403332e-05, ...,\n" | 43 | "array([ 0.00000000e+00, 9.41753387e-06, 3.76403332e-05, ...,\n" |
| 14 | " 8.46982002e-05, 3.76403332e-05, 9.41753387e-06], dtype=float32)"; | 44 | " 8.46982002e-05, 3.76403332e-05, 9.41753387e-06], dtype=float32)\n" |
| 45 | "\n" | ||
| 46 | "Plot different window types with `matplotlib <https://matplotlib.org/>`_:\n" | ||
| 47 | "\n" | ||
| 48 | ">>> import matplotlib.pyplot as plt\n" | ||
| 49 | ">>> modes = ['default', 'ones', 'rectangle', 'hamming', 'hanning',\n" | ||
| 50 | "... 'hanningz', 'blackman', 'blackman_harris', 'gaussian',\n" | ||
| 51 | "... 'welch', 'parzen']; n = 2048\n" | ||
| 52 | ">>> for m in modes: plt.plot(aubio.window(m, n), label=m)\n" | ||
| 53 | "...\n" | ||
| 54 | ">>> plt.legend(); plt.show()\n" | ||
| 55 | "\n" | ||
| 56 | "Note\n" | ||
| 57 | "----\n" | ||
| 58 | "The following examples contain the equivalent source code to compute\n" | ||
| 59 | "each type of window with `NumPy <https://numpy.org>`_:\n" | ||
| 60 | "\n" | ||
| 61 | ">>> n = 1024; x = np.arange(n, dtype=aubio.float_type)\n" | ||
| 62 | ">>> ones = np.ones(n).astype(aubio.float_type)\n" | ||
| 63 | ">>> rectangle = 0.5 * ones\n" | ||
| 64 | ">>> hanning = 0.5 - 0.5 * np.cos(2 * np.pi * x / n)\n" | ||
| 65 | ">>> hanningz = 0.5 * (1 - np.cos(2 * np.pi * x / n))\n" | ||
| 66 | ">>> hamming = 0.54 - 0.46 * np.cos(2.*np.pi * x / (n - 1))\n" | ||
| 67 | ">>> blackman = 0.42 \\\n" | ||
| 68 | "... - 0.50 * np.cos(2 * np.pi * x / (n - 1)) \\\n" | ||
| 69 | "... + 0.08 * np.cos(4 * np.pi * x / (n - 1))\n" | ||
| 70 | ">>> blackman_harris = 0.35875 \\\n" | ||
| 71 | "... - 0.48829 * np.cos(2 * np.pi * x / (n - 1)) \\\n" | ||
| 72 | "... + 0.14128 * np.cos(4 * np.pi * x / (n - 1)) \\\n" | ||
| 73 | "... + 0.01168 * np.cos(6 * np.pi * x / (n - 1))\n" | ||
| 74 | ">>> gaussian = np.exp( - 0.5 * ((x - 0.5 * (n - 1)) \\\n" | ||
| 75 | "... / (0.25 * (n - 1)) )**2 )\n" | ||
| 76 | ">>> welch = 1 - ((2 * x - n) / (n + 1))**2\n" | ||
| 77 | ">>> parzen = 1 - np.abs((2 * x - n) / (n + 1))\n" | ||
| 78 | ">>> default = hanningz\n" | ||
| 79 | "References\n" | ||
| 80 | "----------\n" | ||
| 81 | #if 0 | ||
| 82 | "`Window function <https://en.wikipedia.org/wiki/Window_function>`_ on\n" | ||
| 83 | "Wikipedia.\n" | ||
| 84 | "\n" | ||
| 85 | #endif | ||
| 86 | ".. [1] Amalia de Götzen, Nicolas Bernardini, and Daniel Arfib. Traditional\n" | ||
| 87 | " (?) implementations of a phase vocoder: the tricks of the trade.\n" | ||
| 88 | " In *Proceedings of the International Conference on Digital Audio\n" | ||
| 89 | " Effects* (DAFx-00), pages 37–44, University of Verona, Italy, 2000.\n" | ||
| 90 | " (`online version <" | ||
| 91 | "https://www.cs.princeton.edu/courses/archive/spr09/cos325/Bernardini.pdf" | ||
| 92 | ">`_).\n" | ||
| 93 | ""; | ||
| 15 | 94 | ||
| 16 | PyObject * Py_aubio_window(PyObject *self, PyObject *args); | 95 | PyObject * Py_aubio_window(PyObject *self, PyObject *args); |
| 17 | 96 | ||
| 18 | static char Py_aubio_level_lin_doc[] = "" | 97 | static char Py_aubio_level_lin_doc[] = "" |
| 19 | "level_lin(fvec) -> fvec\n" | 98 | "level_lin(x)\n" |
| 20 | "\n" | 99 | "\n" |
| 21 | "Compute sound level on a linear scale.\n" | 100 | "Compute sound pressure level of `x`, on a linear scale.\n" |
| 22 | "\n" | 101 | "\n" |
| 23 | "This gives the average of the square amplitudes.\n" | 102 | "Parameters\n" |
| 103 | "----------\n" | ||
| 104 | "x : fvec\n" | ||
| 105 | " input vector\n" | ||
| 106 | "\n" | ||
| 107 | "Returns\n" | ||
| 108 | "-------\n" | ||
| 109 | "float\n" | ||
| 110 | " Linear level of `x`.\n" | ||
| 24 | "\n" | 111 | "\n" |
| 25 | "Example\n" | 112 | "Example\n" |
| 26 | "-------\n" | 113 | "-------\n" |
| 27 | "\n" | 114 | "\n" |
| 28 | ">>> level_Lin(numpy.ones(1024))\n" | 115 | ">>> aubio.level_lin(aubio.fvec(numpy.ones(1024)))\n" |
| 29 | "1.0"; | 116 | "1.0\n" |
| 117 | "\n" | ||
| 118 | "Note\n" | ||
| 119 | "----\n" | ||
| 120 | "Computed as the average of the squared amplitudes:\n" | ||
| 121 | "\n" | ||
| 122 | ".. math:: L = \\frac {\\sum_{n=0}^{N-1} {x_n}^2} {N}\n" | ||
| 123 | "\n" | ||
| 124 | "See Also\n" | ||
| 125 | "--------\n" | ||
| 126 | "db_spl, silence_detection, level_detection\n" | ||
| 127 | ""; | ||
| 30 | 128 | ||
| 31 | PyObject * Py_aubio_level_lin(PyObject *self, PyObject *args); | 129 | PyObject * Py_aubio_level_lin(PyObject *self, PyObject *args); |
| 32 | 130 | ||
| 33 | static char Py_aubio_db_spl_doc[] = "" | 131 | static char Py_aubio_db_spl_doc[] = "" |
| 34 | "Compute sound pressure level (SPL) in dB\n" | 132 | "db_spl(x)\n" |
| 35 | "\n" | 133 | "\n" |
| 36 | "This quantity is often wrongly called 'loudness'.\n" | 134 | "Compute Sound Pressure Level (SPL) of `x`, in dB.\n" |
| 37 | "\n" | 135 | "\n" |
| 38 | "This gives ten times the log10 of the average of the square amplitudes.\n" | 136 | "Parameters\n" |
| 137 | "----------\n" | ||
| 138 | "x : fvec\n" | ||
| 139 | " input vector\n" | ||
| 140 | "\n" | ||
| 141 | "Returns\n" | ||
| 142 | "-------\n" | ||
| 143 | "float\n" | ||
| 144 | " Level of `x`, in dB SPL.\n" | ||
| 39 | "\n" | 145 | "\n" |
| 40 | "Example\n" | 146 | "Example\n" |
| 41 | "-------\n" | 147 | "-------\n" |
| 42 | "\n" | 148 | "\n" |
| 43 | ">>> db_spl(numpy.ones(1024))\n" | 149 | ">>> aubio.db_spl(aubio.fvec(np.ones(1024)))\n" |
| 44 | "1.0"; | 150 | "1.0\n" |
| 151 | ">>> aubio.db_spl(0.7*aubio.fvec(np.ones(32)))\n" | ||
| 152 | "-3.098040819168091\n" | ||
| 153 | "\n" | ||
| 154 | "Note\n" | ||
| 155 | "----\n" | ||
| 156 | "Computed as `log10` of :py:func:`level_lin`:\n" | ||
| 157 | "\n" | ||
| 158 | ".. math::\n" | ||
| 159 | "\n" | ||
| 160 | " {SPL}_{dB} = log10{\\frac {\\sum_{n=0}^{N-1}{x_n}^2} {N}}\n" | ||
| 161 | "\n" | ||
| 162 | "This quantity is often incorrectly called 'loudness'.\n" | ||
| 163 | "\n" | ||
| 164 | "See Also\n" | ||
| 165 | "--------\n" | ||
| 166 | "level_lin, silence_detection, level_detection\n" | ||
| 167 | ""; | ||
| 45 | 168 | ||
| 46 | PyObject * Py_aubio_db_spl(PyObject *self, PyObject *args); | 169 | PyObject * Py_aubio_db_spl(PyObject *self, PyObject *args); |
| 47 | 170 | ||
| 48 | static char Py_aubio_silence_detection_doc[] = "" | 171 | static char Py_aubio_silence_detection_doc[] = "" |
| 49 | "Check if buffer level in dB SPL is under a given threshold\n" | 172 | "silence_detection(vec, level)\n" |
| 50 | "\n" | 173 | "\n" |
| 51 | "Return 0 if level is under the given threshold, 1 otherwise.\n" | 174 | "Check if level of `vec`, in dB SPL, is under a given threshold.\n" |
| 52 | "\n" | 175 | "\n" |
| 53 | "Example\n" | 176 | "Parameters\n" |
| 177 | "----------\n" | ||
| 178 | "vec : fvec\n" | ||
| 179 | " input vector\n" | ||
| 180 | "level : float\n" | ||
| 181 | " level threshold, in dB SPL\n" | ||
| 182 | "\n" | ||
| 183 | "Returns\n" | ||
| 54 | "-------\n" | 184 | "-------\n" |
| 185 | "int\n" | ||
| 186 | " `1` if level of `vec`, in dB SPL, is under `level`,\n" | ||
| 187 | " `0` otherwise.\n" | ||
| 55 | "\n" | 188 | "\n" |
| 56 | ">>> import numpy\n""" | 189 | "Examples\n" |
| 57 | ">>> silence_detection(numpy.ones(1024, dtype=\"float32\"), -80)\n" | 190 | "--------\n" |
| 58 | "0"; | 191 | "\n" |
| 192 | ">>> aubio.silence_detection(aubio.fvec(32), -100.)\n" | ||
| 193 | "1\n" | ||
| 194 | ">>> aubio.silence_detection(aubio.fvec(np.ones(32)), 0.)\n" | ||
| 195 | "0\n" | ||
| 196 | "\n" | ||
| 197 | "See Also\n" | ||
| 198 | "--------\n" | ||
| 199 | "level_detection, db_spl, level_lin\n" | ||
| 200 | ""; | ||
| 59 | 201 | ||
| 60 | PyObject * Py_aubio_silence_detection(PyObject *self, PyObject *args); | 202 | PyObject * Py_aubio_silence_detection(PyObject *self, PyObject *args); |
| 61 | 203 | ||
| 62 | static char Py_aubio_level_detection_doc[] = "" | 204 | static char Py_aubio_level_detection_doc[] = "" |
| 63 | "Get buffer level in dB SPL if over a given threshold, 1. otherwise.\n" | 205 | "level_detection(vec, level)\n" |
| 206 | "\n" | ||
| 207 | "Check if `vec` is above threshold `level`, in dB SPL.\n" | ||
| 208 | "\n" | ||
| 209 | "Parameters\n" | ||
| 210 | "----------\n" | ||
| 211 | "vec : fvec\n" | ||
| 212 | " input vector\n" | ||
| 213 | "level : float\n" | ||
| 214 | " level threshold, in dB SPL\n" | ||
| 215 | "\n" | ||
| 216 | "Returns\n" | ||
| 217 | "-------\n" | ||
| 218 | "float\n" | ||
| 219 | " `1.0` if level of `vec` in dB SPL is under `level`,\n" | ||
| 220 | " `db_spl(vec)` otherwise.\n" | ||
| 64 | "\n" | 221 | "\n" |
| 65 | "Example\n" | 222 | "Example\n" |
| 66 | "-------\n" | 223 | "-------\n" |
| 67 | "\n" | 224 | "\n" |
| 68 | ">>> import numpy\n""" | 225 | ">>> aubio.level_detection(0.7*aubio.fvec(np.ones(1024)), -3.)\n" |
| 69 | ">>> level_detection(0.7*numpy.ones(1024, dtype=\"float32\"), -80)\n" | 226 | "1.0\n" |
| 70 | "0"; | 227 | ">>> aubio.level_detection(0.7*aubio.fvec(np.ones(1024)), -4.)\n" |
| 228 | "-3.0980708599090576\n" | ||
| 229 | "\n" | ||
| 230 | "See Also\n" | ||
| 231 | "--------\n" | ||
| 232 | "silence_detection, db_spl, level_lin\n" | ||
| 233 | ""; | ||
| 71 | 234 | ||
| 72 | PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args); | 235 | PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args); |
| 73 | 236 | ||
| 237 | static char Py_aubio_shift_doc[] = "" | ||
| 238 | "shift(vec)\n" | ||
| 239 | "\n" | ||
| 240 | "Swap left and right partitions of a vector, in-place.\n" | ||
| 241 | "\n" | ||
| 242 | "Parameters\n" | ||
| 243 | "----------\n" | ||
| 244 | "vec : fvec\n" | ||
| 245 | " input vector to shift\n" | ||
| 246 | "\n" | ||
| 247 | "Returns\n" | ||
| 248 | "-------\n" | ||
| 249 | "fvec\n" | ||
| 250 | " The swapped vector.\n" | ||
| 251 | "\n" | ||
| 252 | "Notes\n" | ||
| 253 | "-----\n" | ||
| 254 | "The input vector is also modified.\n" | ||
| 255 | "\n" | ||
| 256 | "For a vector of length N, the partition is split at index N - N//2.\n" | ||
| 257 | "\n" | ||
| 258 | "Example\n" | ||
| 259 | "-------\n" | ||
| 260 | "\n" | ||
| 261 | ">>> aubio.shift(aubio.fvec(np.arange(3)))\n" | ||
| 262 | "array([2., 0., 1.], dtype=" AUBIO_NPY_SMPL_STR ")\n" | ||
| 263 | "\n" | ||
| 264 | "See Also\n" | ||
| 265 | "--------\n" | ||
| 266 | "ishift\n" | ||
| 267 | ""; | ||
| 268 | PyObject * Py_aubio_shift(PyObject *self, PyObject *args); | ||
| 269 | |||
| 270 | static char Py_aubio_ishift_doc[] = "" | ||
| 271 | "ishift(vec)\n" | ||
| 272 | "\n" | ||
| 273 | "Swap right and left partitions of a vector, in-place.\n" | ||
| 274 | "\n" | ||
| 275 | "Parameters\n" | ||
| 276 | "----------\n" | ||
| 277 | "vec : fvec\n" | ||
| 278 | " input vector to shift\n" | ||
| 279 | "\n" | ||
| 280 | "Returns\n" | ||
| 281 | "-------\n" | ||
| 282 | "fvec\n" | ||
| 283 | " The swapped vector.\n" | ||
| 284 | "\n" | ||
| 285 | "Notes\n" | ||
| 286 | "-----\n" | ||
| 287 | "The input vector is also modified.\n" | ||
| 288 | "\n" | ||
| 289 | "Unlike with :py:func:`shift`, the partition is split at index N//2.\n" | ||
| 290 | "\n" | ||
| 291 | "Example\n" | ||
| 292 | "-------\n" | ||
| 293 | "\n" | ||
| 294 | ">>> aubio.ishift(aubio.fvec(np.arange(3)))\n" | ||
| 295 | "array([1., 2., 0.], dtype=" AUBIO_NPY_SMPL_STR ")\n" | ||
| 296 | "\n" | ||
| 297 | "See Also\n" | ||
| 298 | "--------\n" | ||
| 299 | "shift\n" | ||
| 300 | ""; | ||
| 301 | PyObject * Py_aubio_ishift(PyObject *self, PyObject *args); | ||
| 302 | |||
| 303 | static char Py_aubio_hztomel_doc[] = "" | ||
| 304 | "hztomel(f, htk=False)\n" | ||
| 305 | "\n" | ||
| 306 | "Convert a scalar from frequency to mel scale.\n" | ||
| 307 | "\n" | ||
| 308 | "Parameters\n" | ||
| 309 | "----------\n" | ||
| 310 | "m : float\n" | ||
| 311 | " input frequency, in Hz\n" | ||
| 312 | "htk : bool\n" | ||
| 313 | " if `True`, use Htk mel scale instead of Slaney.\n" | ||
| 314 | "\n" | ||
| 315 | "Returns\n" | ||
| 316 | "-------\n" | ||
| 317 | "float\n" | ||
| 318 | " output mel\n" | ||
| 319 | "\n" | ||
| 320 | "See Also\n" | ||
| 321 | "--------\n" | ||
| 322 | "meltohz\n" | ||
| 323 | ""; | ||
| 324 | PyObject * Py_aubio_hztomel(PyObject *self, PyObject *args); | ||
| 325 | |||
| 326 | static char Py_aubio_meltohz_doc[] = "" | ||
| 327 | "meltohz(m, htk=False)\n" | ||
| 328 | "\n" | ||
| 329 | "Convert a scalar from mel scale to frequency.\n" | ||
| 330 | "\n" | ||
| 331 | "Parameters\n" | ||
| 332 | "----------\n" | ||
| 333 | "m : float\n" | ||
| 334 | " input mel\n" | ||
| 335 | "htk : bool\n" | ||
| 336 | " if `True`, use Htk mel scale instead of Slaney.\n" | ||
| 337 | "\n" | ||
| 338 | "Returns\n" | ||
| 339 | "-------\n" | ||
| 340 | "float\n" | ||
| 341 | " output frequency, in Hz\n" | ||
| 342 | "\n" | ||
| 343 | "See Also\n" | ||
| 344 | "--------\n" | ||
| 345 | "hztomel\n" | ||
| 346 | ""; | ||
| 347 | PyObject * Py_aubio_meltohz(PyObject *self, PyObject *args); | ||
| 348 | |||
| 349 | static char Py_aubio_hztomel_htk_doc[] = "" | ||
| 350 | "hztomel_htk(m)\n" | ||
| 351 | "\n" | ||
| 352 | "Same as `hztomel(m, htk=True)`\n" | ||
| 353 | "\n" | ||
| 354 | "See Also\n" | ||
| 355 | "--------\n" | ||
| 356 | "hztomel\n" | ||
| 357 | ""; | ||
| 358 | PyObject * Py_aubio_hztomel_htk(PyObject *self, PyObject *args); | ||
| 359 | |||
| 360 | static char Py_aubio_meltohz_htk_doc[] = "" | ||
| 361 | "meltohz_htk(m)\n" | ||
| 362 | "\n" | ||
| 363 | "Same as `meltohz(m, htk=True)`\n" | ||
| 364 | "\n" | ||
| 365 | "See Also\n" | ||
| 366 | "--------\n" | ||
| 367 | "meltohz\n" | ||
| 368 | ""; | ||
| 369 | PyObject * Py_aubio_meltohz_htk(PyObject *self, PyObject *args); | ||
| 370 | |||
| 74 | #endif /* PY_AUBIO_MUSICUTILS_H */ | 371 | #endif /* PY_AUBIO_MUSICUTILS_H */ |
| @@ -1,6 +1,58 @@ | |||
| 1 | #include "aubio-types.h" | 1 | #include "aubio-types.h" |
| 2 | 2 | ||
| 3 | static char Py_pvoc_doc[] = "pvoc object"; | 3 | static char Py_pvoc_doc[] = "" |
| 4 | "pvoc(win_s=512, hop_s=256)\n" | ||
| 5 | "\n" | ||
| 6 | "Phase vocoder.\n" | ||
| 7 | "\n" | ||
| 8 | "`pvoc` creates callable object implements a phase vocoder [1]_,\n" | ||
| 9 | "using the tricks detailed in [2]_.\n" | ||
| 10 | "\n" | ||
| 11 | "The call function takes one input of type `fvec` and of size\n" | ||
| 12 | "`hop_s`, and returns a `cvec` of length `win_s//2+1`.\n" | ||
| 13 | "\n" | ||
| 14 | "Parameters\n" | ||
| 15 | "----------\n" | ||
| 16 | "win_s : int\n" | ||
| 17 | " number of channels in the phase-vocoder.\n" | ||
| 18 | "hop_s : int\n" | ||
| 19 | " number of samples expected between each call\n" | ||
| 20 | "\n" | ||
| 21 | "Examples\n" | ||
| 22 | "--------\n" | ||
| 23 | ">>> x = aubio.fvec(256)\n" | ||
| 24 | ">>> pv = aubio.pvoc(512, 256)\n" | ||
| 25 | ">>> pv(x)\n" | ||
| 26 | "aubio cvec of 257 elements\n" | ||
| 27 | "\n" | ||
| 28 | "Default values for hop_s and win_s are provided:\n" | ||
| 29 | "\n" | ||
| 30 | ">>> pv = aubio.pvoc()\n" | ||
| 31 | ">>> pv.win_s, pv.hop_s\n" | ||
| 32 | "512, 256\n" | ||
| 33 | "\n" | ||
| 34 | "A `cvec` can be resynthesised using `rdo()`:\n" | ||
| 35 | "\n" | ||
| 36 | ">>> pv = aubio.pvoc(512, 256)\n" | ||
| 37 | ">>> y = aubio.cvec(512)\n" | ||
| 38 | ">>> x_reconstructed = pv.rdo(y)\n" | ||
| 39 | ">>> x_reconstructed.shape\n" | ||
| 40 | "(256,)\n" | ||
| 41 | "\n" | ||
| 42 | "References\n" | ||
| 43 | "----------\n" | ||
| 44 | ".. [1] James A. Moorer. The use of the phase vocoder in computer music\n" | ||
| 45 | " applications. `Journal of the Audio Engineering Society`,\n" | ||
| 46 | " 26(1/2):42–45, 1978.\n" | ||
| 47 | ".. [2] Amalia de Götzen, Nicolas Bernardini, and Daniel Arfib. Traditional\n" | ||
| 48 | " (?) implementations of a phase vocoder: the tricks of the trade.\n" | ||
| 49 | " In `Proceedings of the International Conference on Digital Audio\n" | ||
| 50 | " Effects` (DAFx-00), pages 37–44, University of Verona, Italy, 2000.\n" | ||
| 51 | " (`online version <" | ||
| 52 | "https://www.cs.princeton.edu/courses/archive/spr09/cos325/Bernardini.pdf" | ||
| 53 | ">`_).\n" | ||
| 54 | ""; | ||
| 55 | |||
| 4 | 56 | ||
| 5 | typedef struct | 57 | typedef struct |
| 6 | { | 58 | { |
| @@ -38,10 +90,6 @@ Py_pvoc_new (PyTypeObject * type, PyObject * args, PyObject * kwds) · 31 unmodified lines | |||
| 38 | self->win_s = Py_default_vector_length; | 90 | self->win_s = Py_default_vector_length; |
| 39 | self->hop_s = Py_default_vector_length/2; | 91 | self->hop_s = Py_default_vector_length/2; |
| 40 | 92 | ||
| 41 | if (self == NULL) { | ||
| 42 | return NULL; | ||
| 43 | } | ||
| 44 | |||
| 45 | if (win_s > 0) { | 93 | if (win_s > 0) { |
| 46 | self->win_s = win_s; | 94 | self->win_s = win_s; |
| 47 | } else if (win_s < 0) { | 95 | } else if (win_s < 0) { |
| @@ -121,9 +169,11 @@ Py_pvoc_do(Py_pvoc * self, PyObject * args) · 73 unmodified lines | |||
| 121 | 169 | ||
| 122 | static PyMemberDef Py_pvoc_members[] = { | 170 | static PyMemberDef Py_pvoc_members[] = { |
| 123 | {"win_s", T_INT, offsetof (Py_pvoc, win_s), READONLY, | 171 | {"win_s", T_INT, offsetof (Py_pvoc, win_s), READONLY, |
| 124 | "size of the window"}, | 172 | "int: Size of phase vocoder analysis windows, in samples.\n" |
| 173 | ""}, | ||
| 125 | {"hop_s", T_INT, offsetof (Py_pvoc, hop_s), READONLY, | 174 | {"hop_s", T_INT, offsetof (Py_pvoc, hop_s), READONLY, |
| 126 | "size of the hop"}, | 175 | "int: Interval between two analysis, in samples.\n" |
| 176 | ""}, | ||
| 127 | { NULL } // sentinel | 177 | { NULL } // sentinel |
| 128 | }; | 178 | }; |
| 129 | 179 | ||
| @@ -155,9 +205,67 @@ Py_pvoc_rdo(Py_pvoc * self, PyObject * args) · 25 unmodified lines | |||
| 155 | return self->routput; | 205 | return self->routput; |
| 156 | } | 206 | } |
| 157 | 207 | ||
| 208 | static PyObject * | ||
| 209 | Pyaubio_pvoc_set_window (Py_pvoc *self, PyObject *args) | ||
| 210 | { | ||
| 211 | uint_t err = 0; | ||
| 212 | char_t *window = NULL; | ||
| 213 | |||
| 214 | if (!PyArg_ParseTuple (args, "s", &window)) { | ||
| 215 | return NULL; | ||
| 216 | } | ||
| 217 | err = aubio_pvoc_set_window (self->o, window); | ||
| 218 | |||
| 219 | if (err > 0) { | ||
| 220 | PyErr_SetString (PyExc_ValueError, "error running aubio_pvoc_set_window"); | ||
| 221 | return NULL; | ||
| 222 | } | ||
| 223 | Py_RETURN_NONE; | ||
| 224 | } | ||
| 225 | |||
| 158 | static PyMethodDef Py_pvoc_methods[] = { | 226 | static PyMethodDef Py_pvoc_methods[] = { |
| 159 | {"rdo", (PyCFunction) Py_pvoc_rdo, METH_VARARGS, | 227 | {"rdo", (PyCFunction) Py_pvoc_rdo, METH_VARARGS, |
| 160 | "synthesis of spectral grain"}, | 228 | "rdo(fftgrain)\n" |
| 229 | "\n" | ||
| 230 | "Read a new spectral grain and resynthesise the next `hop_s`\n" | ||
| 231 | "output samples.\n" | ||
| 232 | "\n" | ||
| 233 | "Parameters\n" | ||
| 234 | "----------\n" | ||
| 235 | "fftgrain : cvec\n" | ||
| 236 | " new input `cvec` to synthesize from, should be of size `win_s/2+1`\n" | ||
| 237 | "\n" | ||
| 238 | "Returns\n" | ||
| 239 | "-------\n" | ||
| 240 | "fvec\n" | ||
| 241 | " re-synthesised output of shape `(hop_s,)`\n" | ||
| 242 | "\n" | ||
| 243 | "Example\n" | ||
| 244 | "-------\n" | ||
| 245 | ">>> pv = aubio.pvoc(2048, 512)\n" | ||
| 246 | ">>> out = pv.rdo(aubio.cvec(2048))\n" | ||
| 247 | ">>> out.shape\n" | ||
| 248 | "(512,)\n" | ||
| 249 | ""}, | ||
| 250 | {"set_window", (PyCFunction) Pyaubio_pvoc_set_window, METH_VARARGS, | ||
| 251 | "set_window(window_type)\n" | ||
| 252 | "\n" | ||
| 253 | "Set window function\n" | ||
| 254 | "\n" | ||
| 255 | "Parameters\n" | ||
| 256 | "----------\n" | ||
| 257 | "window_type : str\n" | ||
| 258 | " the window type to use for this phase vocoder\n" | ||
| 259 | "\n" | ||
| 260 | "Raises\n" | ||
| 261 | "------\n" | ||
| 262 | "ValueError\n" | ||
| 263 | " If an unknown window type was given.\n" | ||
| 264 | "\n" | ||
| 265 | "See Also\n" | ||
| 266 | "--------\n" | ||
| 267 | "window : create a window.\n" | ||
| 268 | ""}, | ||
| 161 | {NULL} | 269 | {NULL} |
| 162 | }; | 270 | }; |
| 163 | 271 | ||
| @@ -12,53 +12,78 @@ typedef struct · 11 unmodified lines | |||
| 12 | } Py_sink; | 12 | } Py_sink; |
| 13 | 13 | ||
| 14 | static char Py_sink_doc[] = "" | 14 | static char Py_sink_doc[] = "" |
| 15 | " __new__(path, samplerate = 44100, channels = 1)\n" | 15 | "sink(path, samplerate=44100, channels=1)\n" |
| 16 | "\n" | 16 | "\n" |
| 17 | " Create a new sink, opening the given path for writing.\n" | 17 | "Write audio samples to file.\n" |
| 18 | "\n" | 18 | "\n" |
| 19 | " Examples\n" | 19 | "Parameters\n" |
| 20 | " --------\n" | 20 | "----------\n" |
| 21 | "path : str\n" | ||
| 22 | " Pathname of the file to be opened for writing.\n" | ||
| 23 | "samplerate : int\n" | ||
| 24 | " Sampling rate of the file, in Hz.\n" | ||
| 25 | "channels : int\n" | ||
| 26 | " Number of channels to create the file with.\n" | ||
| 21 | "\n" | 27 | "\n" |
| 22 | " Create a new sink at 44100Hz, mono:\n" | 28 | "Examples\n" |
| 29 | "--------\n" | ||
| 23 | "\n" | 30 | "\n" |
| 24 | " >>> sink('/tmp/t.wav')\n" | 31 | "Create a new sink at 44100Hz, mono:\n" |
| 25 | "\n" | 32 | "\n" |
| 26 | " Create a new sink at 8000Hz, mono:\n" | 33 | ">>> snk = aubio.sink('out.wav')\n" |
| 27 | "\n" | 34 | "\n" |
| 28 | " >>> sink('/tmp/t.wav', samplerate = 8000)\n" | 35 | "Create a new sink at 32000Hz, stereo, write 100 samples into it:\n" |
| 29 | "\n" | 36 | "\n" |
| 30 | " Create a new sink at 32000Hz, stereo:\n" | 37 | ">>> snk = aubio.sink('out.wav', samplerate=16000, channels=3)\n" |
| 38 | ">>> snk(aubio.fvec(100), 100)\n" | ||
| 31 | "\n" | 39 | "\n" |
| 32 | " >>> sink('/tmp/t.wav', samplerate = 32000, channels = 2)\n" | 40 | "Open a new sink at 48000Hz, stereo, write `1234` samples into it:\n" |
| 33 | "\n" | 41 | "\n" |
| 34 | " Create a new sink at 32000Hz, 5 channels:\n" | 42 | ">>> with aubio.sink('out.wav', samplerate=48000, channels=2) as src:\n" |
| 43 | "... snk(aubio.fvec(1024), 1024)\n" | ||
| 44 | "... snk(aubio.fvec(210), 210)\n" | ||
| 45 | "...\n" | ||
| 35 | "\n" | 46 | "\n" |
| 36 | " >>> sink('/tmp/t.wav', channels = 5, samplerate = 32000)\n" | 47 | "See also\n" |
| 37 | "\n" | 48 | "--------\n" |
| 38 | " __call__(vec, write)\n" | 49 | "source: read audio samples from a file.\n" |
| 39 | " x(vec,write) <==> x.do(vec, write)\n" | ||
| 40 | "\n" | ||
| 41 | " Write vector to sink.\n" | ||
| 42 | "\n" | ||
| 43 | " See also\n" | ||
| 44 | " --------\n" | ||
| 45 | " aubio.sink.do\n" | ||
| 46 | "\n"; | 50 | "\n"; |
| 47 | 51 | ||
| 48 | static char Py_sink_do_doc[] = "" | 52 | static char Py_sink_do_doc[] = "" |
| 49 | "x.do(vec, write) <==> x(vec, write)\n" | 53 | "do(vec, write)\n" |
| 50 | "\n" | 54 | "\n" |
| 51 | "write monophonic vector to sink"; | 55 | "Write a single channel vector to sink.\n" |
| 56 | "\n" | ||
| 57 | "Parameters\n" | ||
| 58 | "----------\n" | ||
| 59 | "vec : fvec\n" | ||
| 60 | " input vector `(n,)` where `n >= 0`.\n" | ||
| 61 | "write : int\n" | ||
| 62 | " Number of samples to write.\n" | ||
| 63 | ""; | ||
| 52 | 64 | ||
| 53 | static char Py_sink_do_multi_doc[] = "" | 65 | static char Py_sink_do_multi_doc[] = "" |
| 54 | "x.do_multi(mat, write)\n" | 66 | "do_multi(mat, write)\n" |
| 67 | "\n" | ||
| 68 | "Write a matrix containing vectors from multiple channels to sink.\n" | ||
| 55 | "\n" | 69 | "\n" |
| 56 | "write polyphonic vector to sink"; | 70 | "Parameters\n" |
| 71 | "----------\n" | ||
| 72 | "mat : numpy.ndarray\n" | ||
| 73 | " input matrix of shape `(channels, n)`, where `n >= 0`.\n" | ||
| 74 | "write : int\n" | ||
| 75 | " Number of frames to write.\n" | ||
| 76 | ""; | ||
| 57 | 77 | ||
| 58 | static char Py_sink_close_doc[] = "" | 78 | static char Py_sink_close_doc[] = "" |
| 59 | "x.close()\n" | 79 | "close()\n" |
| 60 | "\n" | 80 | "\n" |
| 61 | "close this sink now"; | 81 | "Close this sink now.\n" |
| 82 | "\n" | ||
| 83 | "By default, the sink will be closed before being deleted.\n" | ||
| 84 | "Explicitely closing a sink can be useful to control the number\n" | ||
| 85 | "of files simultaneously opened.\n" | ||
| 86 | ""; | ||
| 62 | 87 | ||
| 63 | static PyObject * | 88 | static PyObject * |
| 64 | Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) | 89 | Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) |
| @@ -80,27 +105,20 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 15 unmodified lines | |||
| 80 | return NULL; | 105 | return NULL; |
| 81 | } | 106 | } |
| 82 | 107 | ||
| 83 | self->uri = "none"; | 108 | self->uri = NULL; |
| 84 | if (uri != NULL) { | 109 | if (uri != NULL) { |
| 85 | self->uri = uri; | 110 | self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1)); |
| 111 | strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1); | ||
| 86 | } | 112 | } |
| 87 | 113 | ||
| 88 | self->samplerate = Py_aubio_default_samplerate; | 114 | self->samplerate = Py_aubio_default_samplerate; |
| 89 | if ((sint_t)samplerate > 0) { | 115 | if (samplerate != 0) { |
| 90 | self->samplerate = samplerate; | 116 | self->samplerate = samplerate; |
| 91 | } else if ((sint_t)samplerate < 0) { | ||
| 92 | PyErr_SetString (PyExc_ValueError, | ||
| 93 | "can not use negative value for samplerate"); | ||
| 94 | return NULL; | ||
| 95 | } | 117 | } |
| 96 | 118 | ||
| 97 | self->channels = 1; | 119 | self->channels = 1; |
| 98 | if ((sint_t)channels > 0) { | 120 | if (channels != 0) { |
| 99 | self->channels = channels; | 121 | self->channels = channels; |
| 100 | } else if ((sint_t)channels < 0) { | ||
| 101 | PyErr_SetString (PyExc_ValueError, | ||
| 102 | "can not use negative or null value for channels"); | ||
| 103 | return NULL; | ||
| 104 | } | 122 | } |
| 105 | 123 | ||
| 106 | return (PyObject *) self; | 124 | return (PyObject *) self; |
| @@ -109,17 +127,20 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 2 unmodified lines | |||
| 109 | static int | 127 | static int |
| 110 | Py_sink_init (Py_sink * self, PyObject * args, PyObject * kwds) | 128 | Py_sink_init (Py_sink * self, PyObject * args, PyObject * kwds) |
| 111 | { | 129 | { |
| 112 | if (self->channels == 1) { | 130 | self->o = new_aubio_sink ( self->uri, 0 ); |
| 113 | self->o = new_aubio_sink ( self->uri, self->samplerate ); | ||
| 114 | } else { | ||
| 115 | self->o = new_aubio_sink ( self->uri, 0 ); | ||
| 116 | aubio_sink_preset_channels ( self->o, self->channels ); | ||
| 117 | aubio_sink_preset_samplerate ( self->o, self->samplerate ); | ||
| 118 | } | ||
| 119 | if (self->o == NULL) { | 131 | if (self->o == NULL) { |
| 120 | PyErr_SetString (PyExc_RuntimeError, "error creating sink with this uri"); | 132 | // error string was set in new_aubio_sink |
| 121 | return -1; | 133 | return -1; |
| 122 | } | 134 | } |
| 135 | if (aubio_sink_preset_channels(self->o, self->channels) != 0) { | ||
| 136 | // error string was set in aubio_sink_preset_channels | ||
| 137 | return -1; | ||
| 138 | } | ||
| 139 | if (aubio_sink_preset_samplerate(self->o, self->samplerate) != 0) { | ||
| 140 | // error string was set in aubio_sink_preset_samplerate | ||
| 141 | return -1; | ||
| 142 | } | ||
| 143 | |||
| 123 | self->samplerate = aubio_sink_get_samplerate ( self->o ); | 144 | self->samplerate = aubio_sink_get_samplerate ( self->o ); |
| 124 | self->channels = aubio_sink_get_channels ( self->o ); | 145 | self->channels = aubio_sink_get_channels ( self->o ); |
| 125 | 146 | ||
| @@ -131,6 +152,9 @@ Py_sink_del (Py_sink *self, PyObject *unused) · 5 unmodified lines | |||
| 131 | { | 152 | { |
| 132 | del_aubio_sink(self->o); | 153 | del_aubio_sink(self->o); |
| 133 | free(self->mwrite_data.data); | 154 | free(self->mwrite_data.data); |
| 155 | if (self->uri) { | ||
| 156 | free(self->uri); | ||
| 157 | } | ||
| 134 | Py_TYPE(self)->tp_free((PyObject *) self); | 158 | Py_TYPE(self)->tp_free((PyObject *) self); |
| 135 | } | 159 | } |
| 136 | 160 | ||
| @@ -189,11 +213,11 @@ Py_sink_do_multi(Py_sink * self, PyObject * args) · 52 unmodified lines | |||
| 189 | 213 | ||
| 190 | static PyMemberDef Py_sink_members[] = { | 214 | static PyMemberDef Py_sink_members[] = { |
| 191 | {"uri", T_STRING, offsetof (Py_sink, uri), READONLY, | 215 | {"uri", T_STRING, offsetof (Py_sink, uri), READONLY, |
| 192 | "path at which the sink was created"}, | 216 | "str (read-only): Path at which the sink was created."}, |
| 193 | {"samplerate", T_INT, offsetof (Py_sink, samplerate), READONLY, | 217 | {"samplerate", T_INT, offsetof (Py_sink, samplerate), READONLY, |
| 194 | "samplerate at which the sink was created"}, | 218 | "int (read-only): Samplerate at which the sink was created."}, |
| 195 | {"channels", T_INT, offsetof (Py_sink, channels), READONLY, | 219 | {"channels", T_INT, offsetof (Py_sink, channels), READONLY, |
| 196 | "number of channels with which the sink was created"}, | 220 | "int (read-only): Number of channels with which the sink was created."}, |
| 197 | { NULL } // sentinel | 221 | { NULL } // sentinel |
| 198 | }; | 222 | }; |
| 199 | 223 | ||
| @@ -204,10 +228,25 @@ Pyaubio_sink_close (Py_sink *self, PyObject *unused) · 4 unmodified lines | |||
| 204 | Py_RETURN_NONE; | 228 | Py_RETURN_NONE; |
| 205 | } | 229 | } |
| 206 | 230 | ||
| 231 | static char Pyaubio_sink_enter_doc[] = ""; | ||
| 232 | static PyObject* Pyaubio_sink_enter(Py_sink *self, PyObject *unused) { | ||
| 233 | Py_INCREF(self); | ||
| 234 | return (PyObject*)self; | ||
| 235 | } | ||
| 236 | |||
| 237 | static char Pyaubio_sink_exit_doc[] = ""; | ||
| 238 | static PyObject* Pyaubio_sink_exit(Py_sink *self, PyObject *unused) { | ||
| 239 | return Pyaubio_sink_close(self, unused); | ||
| 240 | } | ||
| 241 | |||
| 207 | static PyMethodDef Py_sink_methods[] = { | 242 | static PyMethodDef Py_sink_methods[] = { |
| 208 | {"do", (PyCFunction) Py_sink_do, METH_VARARGS, Py_sink_do_doc}, | 243 | {"do", (PyCFunction) Py_sink_do, METH_VARARGS, Py_sink_do_doc}, |
| 209 | {"do_multi", (PyCFunction) Py_sink_do_multi, METH_VARARGS, Py_sink_do_multi_doc}, | 244 | {"do_multi", (PyCFunction) Py_sink_do_multi, METH_VARARGS, Py_sink_do_multi_doc}, |
| 210 | {"close", (PyCFunction) Pyaubio_sink_close, METH_NOARGS, Py_sink_close_doc}, | 245 | {"close", (PyCFunction) Pyaubio_sink_close, METH_NOARGS, Py_sink_close_doc}, |
| 246 | {"__enter__", (PyCFunction)Pyaubio_sink_enter, METH_NOARGS, | ||
| 247 | Pyaubio_sink_enter_doc}, | ||
| 248 | {"__exit__", (PyCFunction)Pyaubio_sink_exit, METH_VARARGS, | ||
| 249 | Pyaubio_sink_exit_doc}, | ||
| 211 | {NULL} /* sentinel */ | 250 | {NULL} /* sentinel */ |
| 212 | }; | 251 | }; |
| 213 | 252 | ||
| @@ -16,68 +16,316 @@ typedef struct · 15 unmodified lines | |||
| 16 | } Py_source; | 16 | } Py_source; |
| 17 | 17 | ||
| 18 | static char Py_source_doc[] = "" | 18 | static char Py_source_doc[] = "" |
| 19 | " __new__(path, samplerate = 0, hop_size = 512, channels = 1)\n" | 19 | "source(path, samplerate=0, hop_size=512, channels=0)\n" |
| 20 | "\n" | 20 | "\n" |
| 21 | " Create a new source, opening the given path for reading.\n" | 21 | "Read audio samples from a media file.\n" |
| 22 | "\n" | 22 | "\n" |
| 23 | " Examples\n" | 23 | "`source` open the file specified in `path` and creates a callable\n" |
| 24 | " --------\n" | 24 | "returning `hop_size` new audio samples at each invocation.\n" |
| 25 | "\n" | 25 | "\n" |
| 26 | " Create a new source, using the original samplerate, with hop_size = 512:\n" | 26 | "If `samplerate=0` (default), the original sampling rate of `path`\n" |
| 27 | "will be used. Otherwise, the output audio samples will be\n" | ||
| 28 | "resampled at the desired sampling-rate.\n" | ||
| 27 | "\n" | 29 | "\n" |
| 28 | " >>> source('/tmp/t.wav')\n" | 30 | "If `channels=0` (default), the original number of channels\n" |
| 31 | "in `path` will be used. Otherwise, the output audio samples\n" | ||
| 32 | "will be down-mixed or up-mixed to the desired number of\n" | ||
| 33 | "channels.\n" | ||
| 29 | "\n" | 34 | "\n" |
| 30 | " Create a new source, resampling the original to 8000Hz:\n" | 35 | "If `path` is a URL, a remote connection will be attempted to\n" |
| 36 | "open the resource and stream data from it.\n" | ||
| 31 | "\n" | 37 | "\n" |
| 32 | " >>> source('/tmp/t.wav', samplerate = 8000)\n" | 38 | "The parameter `hop_size` determines how many samples should be\n" |
| 39 | "read at each consecutive calls.\n" | ||
| 33 | "\n" | 40 | "\n" |
| 34 | " Create a new source, resampling it at 32000Hz, hop_size = 32:\n" | 41 | "Parameters\n" |
| 42 | "----------\n" | ||
| 43 | "path : str\n" | ||
| 44 | " pathname (or URL) of the file to be opened for reading\n" | ||
| 45 | "samplerate : int, optional\n" | ||
| 46 | " sampling rate of the file\n" | ||
| 47 | "hop_size : int, optional\n" | ||
| 48 | " number of samples to be read per iteration\n" | ||
| 49 | "channels : int, optional\n" | ||
| 50 | " number of channels of the file\n" | ||
| 35 | "\n" | 51 | "\n" |
| 36 | " >>> source('/tmp/t.wav', samplerate = 32000, hop_size = 32)\n" | 52 | "Examples\n" |
| 53 | "--------\n" | ||
| 54 | "By default, when only `path` is given, the file will be opened\n" | ||
| 55 | "with its original sampling rate and channel:\n" | ||
| 37 | "\n" | 56 | "\n" |
| 38 | " Create a new source, using its original samplerate:\n" | 57 | ">>> src = aubio.source('stereo.wav')\n" |
| 58 | ">>> src.uri, src.samplerate, src.channels, src.duration\n" | ||
| 59 | "('stereo.wav', 48000, 2, 86833)\n" | ||
| 39 | "\n" | 60 | "\n" |
| 40 | " >>> source('/tmp/t.wav', samplerate = 0)\n" | 61 | "A typical loop to read all samples from a local file could\n" |
| 62 | "look like this:\n" | ||
| 41 | "\n" | 63 | "\n" |
| 42 | " __call__()\n" | 64 | ">>> src = aubio.source('stereo.wav')\n" |
| 43 | " vec, read = x() <==> vec, read = x.do()\n" | 65 | ">>> total_read = 0\n" |
| 66 | ">>> while True:\n" | ||
| 67 | "... samples, read = src()\n" | ||
| 68 | "... # do something with samples\n" | ||
| 69 | "... total_read += read\n" | ||
| 70 | "... if read < src.hop_size:\n" | ||
| 71 | "... break\n" | ||
| 72 | "...\n" | ||
| 44 | "\n" | 73 | "\n" |
| 45 | " Read vector from source.\n" | 74 | "In a more Pythonic way, it can also look like this:\n" |
| 46 | "\n" | 75 | "\n" |
| 47 | " See also\n" | 76 | ">>> total_read = 0\n" |
| 48 | " --------\n" | 77 | ">>> with aubio.source('stereo.wav') as src:\n" |
| 49 | " aubio.source.do\n" | 78 | "... for frames in src:\n" |
| 50 | "\n"; | 79 | "... total_read += samples.shape[-1]\n" |
| 80 | "...\n" | ||
| 81 | "\n" | ||
| 82 | ".. rubric:: Basic interface\n" | ||
| 83 | "\n" | ||
| 84 | "`source` is a **callable**; its :meth:`__call__` method\n" | ||
| 85 | "returns a tuple containing:\n" | ||
| 86 | "\n" | ||
| 87 | "- a vector of shape `(hop_size,)`, filled with the `read` next\n" | ||
| 88 | " samples available, zero-padded if `read < hop_size`\n" | ||
| 89 | "- `read`, an integer indicating the number of samples read\n" | ||
| 90 | "\n" | ||
| 91 | "To read the first `hop_size` samples from the source, simply call\n" | ||
| 92 | "the instance itself, with no argument:\n" | ||
| 93 | "\n" | ||
| 94 | ">>> src = aubio.source('song.ogg')\n" | ||
| 95 | ">>> samples, read = src()\n" | ||
| 96 | ">>> samples.shape, read, src.hop_size\n" | ||
| 97 | "((512,), 512, 512)\n" | ||
| 98 | "\n" | ||
| 99 | "The first call returned the slice of samples `[0 : hop_size]`.\n" | ||
| 100 | "The next call will return samples `[hop_size: 2*hop_size]`.\n" | ||
| 101 | "\n" | ||
| 102 | "After several invocations of :meth:`__call__`, when reaching the end\n" | ||
| 103 | "of the opened stream, `read` might become less than `hop_size`:\n" | ||
| 104 | "\n" | ||
| 105 | ">>> samples, read = src()\n" | ||
| 106 | ">>> samples.shape, read\n" | ||
| 107 | "((512,), 354)\n" | ||
| 108 | "\n" | ||
| 109 | "The end of the vector `samples` is filled with zeros.\n" | ||
| 110 | "\n" | ||
| 111 | "After the end of the stream, `read` will be `0` since no more\n" | ||
| 112 | "samples are available:\n" | ||
| 113 | "\n" | ||
| 114 | ">>> samples, read = src()\n" | ||
| 115 | ">>> samples.shape, read\n" | ||
| 116 | "((512,), 0)\n" | ||
| 117 | "\n" | ||
| 118 | "**Note**: when the source has more than one channels, they\n" | ||
| 119 | "are be down-mixed to mono when invoking :meth:`__call__`.\n" | ||
| 120 | "To read from each individual channel, see :meth:`__next__`.\n" | ||
| 121 | "\n" | ||
| 122 | ".. rubric:: ``for`` statements\n" | ||
| 123 | "\n" | ||
| 124 | "The `source` objects are **iterables**. This allows using them\n" | ||
| 125 | "directly in a ``for`` loop, which calls :meth:`__next__` until\n" | ||
| 126 | "the end of the stream is reached:\n" | ||
| 127 | "\n" | ||
| 128 | ">>> src = aubio.source('stereo.wav')\n" | ||
| 129 | ">>> for frames in src:\n" | ||
| 130 | ">>> print (frames.shape)\n" | ||
| 131 | "...\n" | ||
| 132 | "(2, 512)\n" | ||
| 133 | "(2, 512)\n" | ||
| 134 | "(2, 230)\n" | ||
| 135 | "\n" | ||
| 136 | "**Note**: When `next(self)` is called on a source with multiple\n" | ||
| 137 | "channels, an array of shape `(channels, read)` is returned,\n" | ||
| 138 | "unlike with :meth:`__call__` which always returns the down-mixed\n" | ||
| 139 | "channels.\n" | ||
| 140 | "\n" | ||
| 141 | "If the file is opened with a single channel, `next(self)` returns\n" | ||
| 142 | "an array of shape `(read,)`:\n" | ||
| 143 | "\n" | ||
| 144 | ">>> src = aubio.source('stereo.wav', channels=1)\n" | ||
| 145 | ">>> next(src).shape\n" | ||
| 146 | "(512,)\n" | ||
| 147 | "\n" | ||
| 148 | ".. rubric:: ``with`` statements\n" | ||
| 149 | "\n" | ||
| 150 | "The `source` objects are **context managers**, which allows using\n" | ||
| 151 | "them in ``with`` statements:\n" | ||
| 152 | "\n" | ||
| 153 | ">>> with aubio.source('audiotrack.wav') as source:\n" | ||
| 154 | "... n_frames=0\n" | ||
| 155 | "... for samples in source:\n" | ||
| 156 | "... n_frames += len(samples)\n" | ||
| 157 | "... print('read', n_frames, 'samples in', samples.shape[0], 'channels',\n" | ||
| 158 | "... 'from file \"%%s\"' %% source.uri)\n" | ||
| 159 | "...\n" | ||
| 160 | "read 239334 samples in 2 channels from file \"audiotrack.wav\"\n" | ||
| 161 | "\n" | ||
| 162 | "The file will be closed before exiting the statement.\n" | ||
| 163 | "\n" | ||
| 164 | "See also the methods implementing the context manager,\n" | ||
| 165 | ":meth:`__enter__` and :meth:`__exit__`.\n" | ||
| 166 | "\n" | ||
| 167 | ".. rubric:: Seeking and closing\n" | ||
| 168 | "\n" | ||
| 169 | "At any time, :meth:`seek` can be used to move to any position in\n" | ||
| 170 | "the file. For instance, to rewind to the start of the stream:\n" | ||
| 171 | "\n" | ||
| 172 | ">>> src.seek(0)\n" | ||
| 173 | "\n" | ||
| 174 | "The opened file will be automatically closed when the object falls\n" | ||
| 175 | "out of scope and is scheduled for garbage collection.\n" | ||
| 176 | "\n" | ||
| 177 | "In some cases, it is useful to manually :meth:`close` a given source,\n" | ||
| 178 | "for instance to limit the number of simultaneously opened files:\n" | ||
| 179 | "\n" | ||
| 180 | ">>> src.close()\n" | ||
| 181 | "\n" | ||
| 182 | ".. rubric:: Input formats\n" | ||
| 183 | "\n" | ||
| 184 | "Depending on how aubio was compiled, :class:`source` may or may not\n" | ||
| 185 | "open certain **files format**. Below are some examples that assume\n" | ||
| 186 | "support for compressed files and remote urls was compiled in:\n" | ||
| 187 | "\n" | ||
| 188 | "- open a local file using its original sampling rate and channels,\n" | ||
| 189 | " and with the default hop size:\n" | ||
| 190 | "\n" | ||
| 191 | ">>> s = aubio.source('sample.wav')\n" | ||
| 192 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" | ||
| 193 | "('sample.wav', 44100, 2, 512)\n" | ||
| 194 | "\n" | ||
| 195 | "- open a local compressed audio file, resampling to 32000Hz if needed:\n" | ||
| 196 | "\n" | ||
| 197 | ">>> s = aubio.source('song.mp3', samplerate=32000)\n" | ||
| 198 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" | ||
| 199 | "('song.mp3', 32000, 2, 512)\n" | ||
| 200 | "\n" | ||
| 201 | "- open a local video file, down-mixing and resampling it to 16kHz:\n" | ||
| 202 | "\n" | ||
| 203 | ">>> s = aubio.source('movie.mp4', samplerate=16000, channels=1)\n" | ||
| 204 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" | ||
| 205 | "('movie.mp4', 16000, 1, 512)\n" | ||
| 206 | "\n" | ||
| 207 | "- open a remote resource, with hop_size = 1024:\n" | ||
| 208 | "\n" | ||
| 209 | ">>> s = aubio.source('https://aubio.org/drum.ogg', hop_size=1024)\n" | ||
| 210 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" | ||
| 211 | "('https://aubio.org/drum.ogg', 48000, 2, 1024)\n" | ||
| 212 | "\n" | ||
| 213 | "See Also\n" | ||
| 214 | "--------\n" | ||
| 215 | "sink: write audio samples to a file.\n" | ||
| 216 | ""; | ||
| 51 | 217 | ||
| 52 | static char Py_source_get_samplerate_doc[] = "" | 218 | static char Py_source_get_samplerate_doc[] = "" |
| 53 | "x.get_samplerate() -> source samplerate\n" | 219 | "get_samplerate()\n" |
| 220 | "\n" | ||
| 221 | "Get sampling rate of source.\n" | ||
| 54 | "\n" | 222 | "\n" |
| 55 | "Get samplerate of source."; | 223 | "Returns\n" |
| 224 | "-------\n" | ||
| 225 | "int\n" | ||
| 226 | " Sampling rate, in Hz.\n" | ||
| 227 | ""; | ||
| 56 | 228 | ||
| 57 | static char Py_source_get_channels_doc[] = "" | 229 | static char Py_source_get_channels_doc[] = "" |
| 58 | "x.get_channels() -> number of channels\n" | 230 | "get_channels()\n" |
| 59 | "\n" | 231 | "\n" |
| 60 | "Get number of channels in source."; | 232 | "Get number of channels in source.\n" |
| 233 | "\n" | ||
| 234 | "Returns\n" | ||
| 235 | "-------\n" | ||
| 236 | "int\n" | ||
| 237 | " Number of channels.\n" | ||
| 238 | ""; | ||
| 61 | 239 | ||
| 62 | static char Py_source_do_doc[] = "" | 240 | static char Py_source_do_doc[] = "" |
| 63 | "vec, read = x.do() <==> vec, read = x()\n" | 241 | "source.do()\n" |
| 242 | "\n" | ||
| 243 | "Read vector of audio samples.\n" | ||
| 244 | "\n" | ||
| 245 | "If the audio stream in the source has more than one channel,\n" | ||
| 246 | "the channels will be down-mixed.\n" | ||
| 247 | "\n" | ||
| 248 | "Returns\n" | ||
| 249 | "-------\n" | ||
| 250 | "samples : numpy.ndarray\n" | ||
| 251 | " `fvec` of size `hop_size` containing the new samples.\n" | ||
| 252 | "read : int\n" | ||
| 253 | " Number of samples read from the source, equals to `hop_size`\n" | ||
| 254 | " before the end-of-file is reached, less when it is reached,\n" | ||
| 255 | " and `0` after.\n" | ||
| 64 | "\n" | 256 | "\n" |
| 65 | "Read monophonic vector from source."; | 257 | "See Also\n" |
| 258 | "--------\n" | ||
| 259 | "do_multi\n" | ||
| 260 | "\n" | ||
| 261 | "Examples\n" | ||
| 262 | "--------\n" | ||
| 263 | ">>> src = aubio.source('sample.wav', hop_size=1024)\n" | ||
| 264 | ">>> src.do()\n" | ||
| 265 | "(array([-0.00123001, -0.00036685, 0.00097106, ..., -0.2031033 ,\n" | ||
| 266 | " -0.2025854 , -0.20221856], dtype=" AUBIO_NPY_SMPL_STR "), 1024)\n" | ||
| 267 | ""; | ||
| 66 | 268 | ||
| 67 | static char Py_source_do_multi_doc[] = "" | 269 | static char Py_source_do_multi_doc[] = "" |
| 68 | "mat, read = x.do_multi()\n" | 270 | "do_multi()\n" |
| 271 | "\n" | ||
| 272 | "Read multiple channels of audio samples.\n" | ||
| 273 | "\n" | ||
| 274 | "If the source was opened with the same number of channels\n" | ||
| 275 | "found in the stream, each channel will be read individually.\n" | ||
| 276 | "\n" | ||
| 277 | "If the source was opened with less channels than the number\n" | ||
| 278 | "of channels in the stream, only the first channels will be read.\n" | ||
| 279 | "\n" | ||
| 280 | "If the source was opened with more channels than the number\n" | ||
| 281 | "of channel in the original stream, the first channels will\n" | ||
| 282 | "be duplicated on the additional output channel.\n" | ||
| 69 | "\n" | 283 | "\n" |
| 70 | "Read polyphonic vector from source."; | 284 | "Returns\n" |
| 285 | "-------\n" | ||
| 286 | "samples : numpy.ndarray\n" | ||
| 287 | " NumPy array of shape `(hop_size, channels)` containing the new\n" | ||
| 288 | " audio samples.\n" | ||
| 289 | "read : int\n" | ||
| 290 | " Number of samples read from the source, equals to `hop_size`\n" | ||
| 291 | " before the end-of-file is reached, less when it is reached,\n" | ||
| 292 | " and `0` after.\n" | ||
| 293 | "\n" | ||
| 294 | "See Also\n" | ||
| 295 | "--------\n" | ||
| 296 | "do\n" | ||
| 297 | "\n" | ||
| 298 | "Examples\n" | ||
| 299 | "--------\n" | ||
| 300 | ">>> src = aubio.source('sample.wav')\n" | ||
| 301 | ">>> src.do_multi()\n" | ||
| 302 | "(array([[ 0.00668335, 0.0067749 , 0.00714111, ..., -0.05737305,\n" | ||
| 303 | " -0.05856323, -0.06018066],\n" | ||
| 304 | " [-0.00842285, -0.0072937 , -0.00576782, ..., -0.09405518,\n" | ||
| 305 | " -0.09558105, -0.09725952]], dtype=" AUBIO_NPY_SMPL_STR "), 512)\n" | ||
| 306 | ""; | ||
| 71 | 307 | ||
| 72 | static char Py_source_close_doc[] = "" | 308 | static char Py_source_close_doc[] = "" |
| 73 | "x.close()\n" | 309 | "close()\n" |
| 74 | "\n" | 310 | "\n" |
| 75 | "Close this source now."; | 311 | "Close this source now.\n" |
| 312 | "\n" | ||
| 313 | ".. note:: Closing twice a source will **not** raise any exception.\n" | ||
| 314 | ""; | ||
| 76 | 315 | ||
| 77 | static char Py_source_seek_doc[] = "" | 316 | static char Py_source_seek_doc[] = "" |
| 78 | "x.seek(position)\n" | 317 | "seek(position)\n" |
| 318 | "\n" | ||
| 319 | "Seek to position in file.\n" | ||
| 79 | "\n" | 320 | "\n" |
| 80 | "Seek to resampled frame position."; | 321 | "If the source was not opened with its original sampling-rate,\n" |
| 322 | "`position` corresponds to the position in the re-sampled file.\n" | ||
| 323 | "\n" | ||
| 324 | "Parameters\n" | ||
| 325 | "----------\n" | ||
| 326 | "position : str\n" | ||
| 327 | " position to seek to, in samples\n" | ||
| 328 | ""; | ||
| 81 | 329 | ||
| 82 | static PyObject * | 330 | static PyObject * |
| 83 | Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) | 331 | Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) |
| @@ -100,9 +348,10 @@ Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 16 unmodified lines | |||
| 100 | return NULL; | 348 | return NULL; |
| 101 | } | 349 | } |
| 102 | 350 | ||
| 103 | self->uri = "none"; | 351 | self->uri = NULL; |
| 104 | if (uri != NULL) { | 352 | if (uri != NULL) { |
| 105 | self->uri = uri; | 353 | self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1)); |
| 354 | strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1); | ||
| 106 | } | 355 | } |
| 107 | 356 | ||
| 108 | self->samplerate = 0; | 357 | self->samplerate = 0; |
| @@ -163,6 +412,9 @@ Py_source_del (Py_source *self, PyObject *unused) · 54 unmodified lines | |||
| 163 | del_aubio_source(self->o); | 412 | del_aubio_source(self->o); |
| 164 | free(self->c_mread_to.data); | 413 | free(self->c_mread_to.data); |
| 165 | } | 414 | } |
| 415 | if (self->uri) { | ||
| 416 | free(self->uri); | ||
| 417 | } | ||
| 166 | Py_XDECREF(self->read_to); | 418 | Py_XDECREF(self->read_to); |
| 167 | Py_XDECREF(self->mread_to); | 419 | Py_XDECREF(self->mread_to); |
| 168 | Py_TYPE(self)->tp_free((PyObject *) self); | 420 | Py_TYPE(self)->tp_free((PyObject *) self); |
| @@ -213,15 +465,29 @@ Py_source_do_multi(Py_source * self, PyObject * args) · 44 unmodified lines | |||
| 213 | 465 | ||
| 214 | static PyMemberDef Py_source_members[] = { | 466 | static PyMemberDef Py_source_members[] = { |
| 215 | {"uri", T_STRING, offsetof (Py_source, uri), READONLY, | 467 | {"uri", T_STRING, offsetof (Py_source, uri), READONLY, |
| 216 | "path at which the source was created"}, | 468 | "str (read-only): pathname or URL"}, |
| 217 | {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY, | 469 | {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY, |
| 218 | "samplerate at which the source is viewed"}, | 470 | "int (read-only): sampling rate"}, |
| 219 | {"channels", T_INT, offsetof (Py_source, channels), READONLY, | 471 | {"channels", T_INT, offsetof (Py_source, channels), READONLY, |
| 220 | "number of channels found in the source"}, | 472 | "int (read-only): number of channels"}, |
| 221 | {"hop_size", T_INT, offsetof (Py_source, hop_size), READONLY, | 473 | {"hop_size", T_INT, offsetof (Py_source, hop_size), READONLY, |
| 222 | "number of consecutive frames that will be read at each do or do_multi call"}, | 474 | "int (read-only): number of samples read per iteration"}, |
| 223 | {"duration", T_INT, offsetof (Py_source, duration), READONLY, | 475 | {"duration", T_INT, offsetof (Py_source, duration), READONLY, |
| 224 | "total number of frames in the source (estimated)"}, | 476 | "int (read-only): total number of frames in the source\n" |
| 477 | "\n" | ||
| 478 | "Can be estimated, for instance if the opened stream is\n" | ||
| 479 | "a compressed media or a remote resource.\n" | ||
| 480 | "\n" | ||
| 481 | "Example\n" | ||
| 482 | "-------\n" | ||
| 483 | ">>> n = 0\n" | ||
| 484 | ">>> src = aubio.source('track1.mp3')\n" | ||
| 485 | ">>> for samples in src:\n" | ||
| 486 | "... n += samples.shape[-1]\n" | ||
| 487 | "...\n" | ||
| 488 | ">>> n, src.duration\n" | ||
| 489 | "(9638784, 9616561)\n" | ||
| 490 | ""}, | ||
| 225 | { NULL } // sentinel | 491 | { NULL } // sentinel |
| 226 | }; | 492 | }; |
| 227 | 493 | ||
| @@ -242,7 +508,7 @@ Pyaubio_source_get_channels (Py_source *self, PyObject *unused) · 14 unmodified lines | |||
| 242 | static PyObject * | 508 | static PyObject * |
| 243 | Pyaubio_source_close (Py_source *self, PyObject *unused) | 509 | Pyaubio_source_close (Py_source *self, PyObject *unused) |
| 244 | { | 510 | { |
| 245 | aubio_source_close (self->o); | 511 | if (aubio_source_close(self->o) != 0) return NULL; |
| 246 | Py_RETURN_NONE; | 512 | Py_RETURN_NONE; |
| 247 | } | 513 | } |
| 248 | 514 | ||
| @@ -272,6 +538,65 @@ Pyaubio_source_seek (Py_source *self, PyObject *args) · 23 unmodified lines | |||
| 272 | Py_RETURN_NONE; | 538 | Py_RETURN_NONE; |
| 273 | } | 539 | } |
| 274 | 540 | ||
| 541 | static char Pyaubio_source_enter_doc[] = ""; | ||
| 542 | static PyObject* Pyaubio_source_enter(Py_source *self, PyObject *unused) { | ||
| 543 | Py_INCREF(self); | ||
| 544 | return (PyObject*)self; | ||
| 545 | } | ||
| 546 | |||
| 547 | static char Pyaubio_source_exit_doc[] = ""; | ||
| 548 | static PyObject* Pyaubio_source_exit(Py_source *self, PyObject *unused) { | ||
| 549 | return Pyaubio_source_close(self, unused); | ||
| 550 | } | ||
| 551 | |||
| 552 | static PyObject* Pyaubio_source_iter(PyObject *self) { | ||
| 553 | Py_INCREF(self); | ||
| 554 | return (PyObject*)self; | ||
| 555 | } | ||
| 556 | |||
| 557 | static PyObject* Pyaubio_source_iter_next(Py_source *self) { | ||
| 558 | PyObject *done, *size; | ||
| 559 | if (self->channels == 1) { | ||
| 560 | done = Py_source_do(self, NULL); | ||
| 561 | } else { | ||
| 562 | done = Py_source_do_multi(self, NULL); | ||
| 563 | } | ||
| 564 | if (!PyTuple_Check(done)) { | ||
| 565 | PyErr_Format(PyExc_ValueError, | ||
| 566 | "error when reading source: not opened?"); | ||
| 567 | return NULL; | ||
| 568 | } | ||
| 569 | size = PyTuple_GetItem(done, 1); | ||
| 570 | if (size != NULL && PyLong_Check(size)) { | ||
| 571 | if (PyLong_AsLong(size) == (long)self->hop_size) { | ||
| 572 | PyObject *vec = PyTuple_GetItem(done, 0); | ||
| 573 | return vec; | ||
| 574 | } else if (PyLong_AsLong(size) > 0) { | ||
| 575 | // short read, return a shorter array | ||
| 576 | PyArrayObject *shortread = (PyArrayObject*)PyTuple_GetItem(done, 0); | ||
| 577 | PyArray_Dims newdims; | ||
| 578 | PyObject *reshaped; | ||
| 579 | newdims.len = PyArray_NDIM(shortread); | ||
| 580 | newdims.ptr = PyArray_DIMS(shortread); | ||
| 581 | // mono or multiple channels? | ||
| 582 | if (newdims.len == 1) { | ||
| 583 | newdims.ptr[0] = PyLong_AsLong(size); | ||
| 584 | } else { | ||
| 585 | newdims.ptr[1] = PyLong_AsLong(size); | ||
| 586 | } | ||
| 587 | reshaped = PyArray_Newshape(shortread, &newdims, NPY_CORDER); | ||
| 588 | Py_DECREF(shortread); | ||
| 589 | return reshaped; | ||
| 590 | } else { | ||
| 591 | PyErr_SetNone(PyExc_StopIteration); | ||
| 592 | return NULL; | ||
| 593 | } | ||
| 594 | } else { | ||
| 595 | PyErr_SetNone(PyExc_StopIteration); | ||
| 596 | return NULL; | ||
| 597 | } | ||
| 598 | } | ||
| 599 | |||
| 275 | static PyMethodDef Py_source_methods[] = { | 600 | static PyMethodDef Py_source_methods[] = { |
| 276 | {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate, | 601 | {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate, |
| 277 | METH_NOARGS, Py_source_get_samplerate_doc}, | 602 | METH_NOARGS, Py_source_get_samplerate_doc}, |
| @@ -285,6 +610,10 @@ static PyMethodDef Py_source_methods[] = { · 7 unmodified lines | |||
| 285 | METH_NOARGS, Py_source_close_doc}, | 610 | METH_NOARGS, Py_source_close_doc}, |
| 286 | {"seek", (PyCFunction) Pyaubio_source_seek, | 611 | {"seek", (PyCFunction) Pyaubio_source_seek, |
| 287 | METH_VARARGS, Py_source_seek_doc}, | 612 | METH_VARARGS, Py_source_seek_doc}, |
| 613 | {"__enter__", (PyCFunction)Pyaubio_source_enter, METH_NOARGS, | ||
| 614 | Pyaubio_source_enter_doc}, | ||
| 615 | {"__exit__", (PyCFunction)Pyaubio_source_exit, METH_VARARGS, | ||
| 616 | Pyaubio_source_exit_doc}, | ||
| 288 | {NULL} /* sentinel */ | 617 | {NULL} /* sentinel */ |
| 289 | }; | 618 | }; |
| 290 | 619 | ||
| @@ -314,8 +643,8 @@ PyTypeObject Py_sourceType = { · 23 unmodified lines | |||
| 314 | 0, | 643 | 0, |
| 315 | 0, | 644 | 0, |
| 316 | 0, | 645 | 0, |
| 317 | 0, | 646 | Pyaubio_source_iter, |
| 318 | 0, | 647 | (unaryfunc)Pyaubio_source_iter_next, |
| 319 | Py_source_methods, | 648 | Py_source_methods, |
| 320 | Py_source_members, | 649 | Py_source_members, |
| 321 | 0, | 650 | 0, |
| @@ -58,7 +58,22 @@ static char Py_aubio_unary_types[] = { · 57 unmodified lines | |||
| 58 | //NPY_OBJECT, NPY_OBJECT, | 58 | //NPY_OBJECT, NPY_OBJECT, |
| 59 | }; | 59 | }; |
| 60 | 60 | ||
| 61 | static char Py_unwrap2pi_doc[] = "map angle to unit circle [-pi, pi["; | 61 | // Note: these docstrings should *not* include the function signatures |
| 62 | |||
| 63 | static char Py_unwrap2pi_doc[] = "" | ||
| 64 | "\n" | ||
| 65 | "Map angle to unit circle :math:`[-\\pi, \\pi[`.\n" | ||
| 66 | "\n" | ||
| 67 | "Parameters\n" | ||
| 68 | "----------\n" | ||
| 69 | "x : numpy.ndarray\n" | ||
| 70 | " input array\n" | ||
| 71 | "\n" | ||
| 72 | "Returns\n" | ||
| 73 | "-------\n" | ||
| 74 | "numpy.ndarray\n" | ||
| 75 | " values clamped to the unit circle :math:`[-\\pi, \\pi[`\n" | ||
| 76 | ""; | ||
| 62 | 77 | ||
| 63 | static void* Py_unwrap2pi_data[] = { | 78 | static void* Py_unwrap2pi_data[] = { |
| 64 | (void *)aubio_unwrap2pi, | 79 | (void *)aubio_unwrap2pi, |
| @@ -67,14 +82,40 @@ static void* Py_unwrap2pi_data[] = { · 2 unmodified lines | |||
| 67 | //(void *)unwrap2pio, | 82 | //(void *)unwrap2pio, |
| 68 | }; | 83 | }; |
| 69 | 84 | ||
| 70 | static char Py_freqtomidi_doc[] = "convert frequency to midi"; | 85 | static char Py_freqtomidi_doc[] = "" |
| 86 | "\n" | ||
| 87 | "Convert frequency `[0; 23000[` to midi `[0; 128[`.\n" | ||
| 88 | "\n" | ||
| 89 | "Parameters\n" | ||
| 90 | "----------\n" | ||
| 91 | "x : numpy.ndarray\n" | ||
| 92 | " Array of frequencies, in Hz.\n" | ||
| 93 | "\n" | ||
| 94 | "Returns\n" | ||
| 95 | "-------\n" | ||
| 96 | "numpy.ndarray\n" | ||
| 97 | " Converted frequencies, in midi note.\n" | ||
| 98 | ""; | ||
| 71 | 99 | ||
| 72 | static void* Py_freqtomidi_data[] = { | 100 | static void* Py_freqtomidi_data[] = { |
| 73 | (void *)aubio_freqtomidi, | 101 | (void *)aubio_freqtomidi, |
| 74 | (void *)aubio_freqtomidi, | 102 | (void *)aubio_freqtomidi, |
| 75 | }; | 103 | }; |
| 76 | 104 | ||
| 77 | static char Py_miditofreq_doc[] = "convert midi to frequency"; | 105 | static char Py_miditofreq_doc[] = "" |
| 106 | "\n" | ||
| 107 | "Convert midi `[0; 128[` to frequency `[0, 23000]`.\n" | ||
| 108 | "\n" | ||
| 109 | "Parameters\n" | ||
| 110 | "----------\n" | ||
| 111 | "x : numpy.ndarray\n" | ||
| 112 | " Array of frequencies, in midi note.\n" | ||
| 113 | "\n" | ||
| 114 | "Returns\n" | ||
| 115 | "-------\n" | ||
| 116 | "numpy.ndarray\n" | ||
| 117 | " Converted frequencies, in Hz\n" | ||
| 118 | ""; | ||
| 78 | 119 | ||
| 79 | static void* Py_miditofreq_data[] = { | 120 | static void* Py_miditofreq_data[] = { |
| 80 | (void *)aubio_miditofreq, | 121 | (void *)aubio_miditofreq, |
Empty file deleted.
| @@ -1,18 +1,85 @@ | |||
| 1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
| 2 | # -*- coding: utf8 -*- | ||
| 3 | |||
| 4 | """ | ||
| 5 | aubio | ||
| 6 | ===== | ||
| 7 | |||
| 8 | Provides a number of classes and functions for music and audio signal | ||
| 9 | analysis. | ||
| 10 | |||
| 11 | How to use the documentation | ||
| 12 | ---------------------------- | ||
| 13 | |||
| 14 | Documentation of the python module is available as docstrings provided | ||
| 15 | within the code, and a reference guide available online from `the | ||
| 16 | aubio homepage <https://aubio.org/documentation>`_. | ||
| 17 | |||
| 18 | The docstrings examples are written assuming `aubio` and `numpy` have been | ||
| 19 | imported with: | ||
| 20 | |||
| 21 | >>> import aubio | ||
| 22 | >>> import numpy as np | ||
| 23 | """ | ||
| 2 | 24 | ||
| 3 | import numpy | 25 | import numpy |
| 4 | from ._aubio import * | 26 | from ._aubio import __version__ as version |
| 5 | from ._aubio import float_type | 27 | from ._aubio import float_type |
| 28 | from ._aubio import * | ||
| 6 | from .midiconv import * | 29 | from .midiconv import * |
| 7 | from .slicing import * | 30 | from .slicing import * |
| 8 | 31 | ||
| 32 | |||
| 9 | class fvec(numpy.ndarray): | 33 | class fvec(numpy.ndarray): |
| 10 | """a numpy vector holding audio samples""" | 34 | """fvec(input_arg=1024) |
| 35 | A vector holding float samples. | ||
| 36 | |||
| 37 | If `input_arg` is an `int`, a 1-dimensional vector of length `input_arg` | ||
| 38 | will be created and filled with zeros. Otherwise, if `input_arg` is an | ||
| 39 | `array_like` object, it will be converted to a 1-dimensional vector of | ||
| 40 | type :data:`float_type`. | ||
| 41 | |||
| 42 | Parameters | ||
| 43 | ---------- | ||
| 44 | input_arg : `int` or `array_like` | ||
| 45 | Can be a positive integer, or any object that can be converted to | ||
| 46 | a numpy array with :func:`numpy.array`. | ||
| 47 | |||
| 48 | Examples | ||
| 49 | -------- | ||
| 50 | >>> aubio.fvec(10) | ||
| 51 | array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32) | ||
| 52 | >>> aubio.fvec([0,1,2]) | ||
| 53 | array([0., 1., 2.], dtype=float32) | ||
| 54 | >>> a = np.arange(10); type(a), type(aubio.fvec(a)) | ||
| 55 | (<class 'numpy.ndarray'>, <class 'numpy.ndarray'>) | ||
| 56 | >>> a.dtype, aubio.fvec(a).dtype | ||
| 57 | (dtype('int64'), dtype('float32')) | ||
| 11 | 58 | ||
| 12 | def __new__(cls, input_arg=1024, **kwargs): | 59 | Notes |
| 60 | ----- | ||
| 61 | |||
| 62 | In the Python world, `fvec` is simply a subclass of | ||
| 63 | :class:`numpy.ndarray`. In practice, any 1-dimensional `numpy.ndarray` of | ||
| 64 | `dtype` :data:`float_type` may be passed to methods accepting | ||
| 65 | `fvec` as parameter. For instance, `sink()` or `pvoc()`. | ||
| 66 | |||
| 67 | See Also | ||
| 68 | -------- | ||
| 69 | cvec : a container holding spectral data | ||
| 70 | numpy.ndarray : parent class of :class:`fvec` | ||
| 71 | numpy.zeros : create a numpy array filled with zeros | ||
| 72 | numpy.array : create a numpy array from an existing object | ||
| 73 | """ | ||
| 74 | def __new__(cls, input_arg=1024): | ||
| 13 | if isinstance(input_arg, int): | 75 | if isinstance(input_arg, int): |
| 14 | if input_arg == 0: | 76 | if input_arg == 0: |
| 15 | raise ValueError("vector length of 1 or more expected") | 77 | raise ValueError("vector length of 1 or more expected") |
| 16 | return numpy.zeros(input_arg, dtype=float_type, **kwargs) | 78 | return numpy.zeros(input_arg, dtype=float_type, order='C') |
| 17 | else: | 79 | else: |
| 18 | return numpy.array(input_arg, dtype=float_type, **kwargs) | 80 | np_input = numpy.array(input_arg, dtype=float_type, order='C') |
| 81 | if len(np_input.shape) != 1: | ||
| 82 | raise ValueError("input_arg should have shape (n,)") | ||
| 83 | if np_input.shape[0] == 0: | ||
| 84 | raise ValueError("vector length of 1 or more expected") | ||
| 85 | return np_input | ||
| @@ -0,0 +1,623 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 3 | |||
| 4 | """aubio command line tool | ||
| 5 | |||
| 6 | This file was written by Paul Brossier <piem@aubio.org> and is released under | ||
| 7 | the GNU/GPL v3. | ||
| 8 | |||
| 9 | Note: this script is mostly about parsing command line arguments. For more | ||
| 10 | readable code examples, check out the `python/demos` folder.""" | ||
| 11 | |||
| 12 | import sys | ||
| 13 | import argparse | ||
| 14 | import warnings | ||
| 15 | import aubio | ||
| 16 | |||
| 17 | def aubio_parser(): | ||
| 18 | epilog = 'use "%(prog)s <command> --help" for more info about each command' | ||
| 19 | parser = argparse.ArgumentParser(epilog=epilog) | ||
| 20 | parser.add_argument('-V', '--version', help="show version", | ||
| 21 | action="store_true", dest="show_version") | ||
| 22 | |||
| 23 | subparsers = parser.add_subparsers(title='commands', dest='command', | ||
| 24 | parser_class= AubioArgumentParser, | ||
| 25 | metavar="") | ||
| 26 | |||
| 27 | parser_add_subcommand_help(subparsers) | ||
| 28 | |||
| 29 | parser_add_subcommand_onset(subparsers) | ||
| 30 | parser_add_subcommand_pitch(subparsers) | ||
| 31 | parser_add_subcommand_beat(subparsers) | ||
| 32 | parser_add_subcommand_tempo(subparsers) | ||
| 33 | parser_add_subcommand_notes(subparsers) | ||
| 34 | parser_add_subcommand_mfcc(subparsers) | ||
| 35 | parser_add_subcommand_melbands(subparsers) | ||
| 36 | parser_add_subcommand_quiet(subparsers) | ||
| 37 | parser_add_subcommand_cut(subparsers) | ||
| 38 | |||
| 39 | return parser | ||
| 40 | |||
| 41 | def parser_add_subcommand_help(subparsers): | ||
| 42 | # global help subcommand | ||
| 43 | subparsers.add_parser('help', | ||
| 44 | help='show help message', | ||
| 45 | formatter_class = argparse.ArgumentDefaultsHelpFormatter) | ||
| 46 | |||
| 47 | def parser_add_subcommand_onset(subparsers): | ||
| 48 | # onset subcommand | ||
| 49 | subparser = subparsers.add_parser('onset', | ||
| 50 | help='estimate time of onsets (beginning of sound event)', | ||
| 51 | formatter_class = argparse.ArgumentDefaultsHelpFormatter) | ||
| 52 | subparser.add_input() | ||
| 53 | subparser.add_buf_hop_size() | ||
| 54 | helpstr = "onset novelty function" | ||
| 55 | helpstr += " <default|energy|hfc|complex|phase|specdiff|kl|mkl|specflux>" | ||
| 56 | subparser.add_method(helpstr=helpstr) | ||
| 57 | subparser.add_threshold() | ||
| 58 | subparser.add_silence() | ||
| 59 | subparser.add_minioi() | ||
| 60 | subparser.add_time_format() | ||
| 61 | subparser.add_verbose_help() | ||
| 62 | subparser.set_defaults(process=process_onset) | ||
| 63 | |||
| 64 | def parser_add_subcommand_pitch(subparsers): | ||
| 65 | # pitch subcommand | ||
| 66 | subparser = subparsers.add_parser('pitch', | ||
| 67 | help='estimate fundamental frequency (monophonic)') | ||
| 68 | subparser.add_input() | ||
| 69 | subparser.add_buf_hop_size(buf_size=2048) | ||
| 70 | helpstr = "pitch detection method <default|yinfft|yin|mcomb|fcomb|schmitt>" | ||
| 71 | subparser.add_method(helpstr=helpstr) | ||
| 72 | subparser.add_threshold() | ||
| 73 | subparser.add_pitch_unit() | ||
| 74 | subparser.add_silence() | ||
| 75 | subparser.add_time_format() | ||
| 76 | subparser.add_verbose_help() | ||
| 77 | subparser.set_defaults(process=process_pitch) | ||
| 78 | |||
| 79 | def parser_add_subcommand_beat(subparsers): | ||
| 80 | # beat subcommand | ||
| 81 | subparser = subparsers.add_parser('beat', | ||
| 82 | help='estimate location of beats') | ||
| 83 | subparser.add_input() | ||
| 84 | subparser.add_buf_hop_size(buf_size=1024, hop_size=512) | ||
| 85 | subparser.add_time_format() | ||
| 86 | subparser.add_verbose_help() | ||
| 87 | subparser.set_defaults(process=process_beat) | ||
| 88 | |||
| 89 | def parser_add_subcommand_tempo(subparsers): | ||
| 90 | # tempo subcommand | ||
| 91 | subparser = subparsers.add_parser('tempo', | ||
| 92 | help='estimate overall tempo in bpm') | ||
| 93 | subparser.add_input() | ||
| 94 | subparser.add_buf_hop_size(buf_size=1024, hop_size=512) | ||
| 95 | subparser.add_time_format() | ||
| 96 | subparser.add_verbose_help() | ||
| 97 | subparser.set_defaults(process=process_tempo) | ||
| 98 | |||
| 99 | def parser_add_subcommand_notes(subparsers): | ||
| 100 | # notes subcommand | ||
| 101 | subparser = subparsers.add_parser('notes', | ||
| 102 | help='estimate midi-like notes (monophonic)') | ||
| 103 | subparser.add_input() | ||
| 104 | subparser.add_buf_hop_size() | ||
| 105 | subparser.add_silence() | ||
| 106 | subparser.add_release_drop() | ||
| 107 | subparser.add_time_format() | ||
| 108 | subparser.add_verbose_help() | ||
| 109 | subparser.set_defaults(process=process_notes) | ||
| 110 | |||
| 111 | def parser_add_subcommand_mfcc(subparsers): | ||
| 112 | # mfcc subcommand | ||
| 113 | subparser = subparsers.add_parser('mfcc', | ||
| 114 | help='extract Mel-Frequency Cepstrum Coefficients') | ||
| 115 | subparser.add_input() | ||
| 116 | subparser.add_buf_hop_size() | ||
| 117 | subparser.add_time_format() | ||
| 118 | subparser.add_verbose_help() | ||
| 119 | subparser.set_defaults(process=process_mfcc) | ||
| 120 | |||
| 121 | def parser_add_subcommand_melbands(subparsers): | ||
| 122 | # melbands subcommand | ||
| 123 | subparser = subparsers.add_parser('melbands', | ||
| 124 | help='extract energies in Mel-frequency bands') | ||
| 125 | subparser.add_input() | ||
| 126 | subparser.add_buf_hop_size() | ||
| 127 | subparser.add_time_format() | ||
| 128 | subparser.add_verbose_help() | ||
| 129 | subparser.set_defaults(process=process_melbands) | ||
| 130 | |||
| 131 | def parser_add_subcommand_quiet(subparsers): | ||
| 132 | # quiet subcommand | ||
| 133 | subparser = subparsers.add_parser('quiet', | ||
| 134 | help='extract timestamps of quiet and loud regions') | ||
| 135 | subparser.add_input() | ||
| 136 | subparser.add_hop_size() | ||
| 137 | subparser.add_silence() | ||
| 138 | subparser.add_time_format() | ||
| 139 | subparser.add_verbose_help() | ||
| 140 | subparser.set_defaults(process=process_quiet) | ||
| 141 | |||
| 142 | def parser_add_subcommand_cut(subparsers): | ||
| 143 | # cut subcommand | ||
| 144 | subparser = subparsers.add_parser('cut', | ||
| 145 | help='slice at timestamps') | ||
| 146 | subparser.add_input() | ||
| 147 | helpstr = "onset novelty function" | ||
| 148 | helpstr += " <default|energy|hfc|complex|phase|specdiff|kl|mkl|specflux>" | ||
| 149 | subparser.add_method(helpstr=helpstr) | ||
| 150 | subparser.add_buf_hop_size() | ||
| 151 | subparser.add_silence() | ||
| 152 | subparser.add_threshold(default=0.3) | ||
| 153 | subparser.add_minioi() | ||
| 154 | subparser.add_slicer_options() | ||
| 155 | subparser.add_time_format() | ||
| 156 | subparser.add_verbose_help() | ||
| 157 | subparser.set_defaults(process=process_cut) | ||
| 158 | |||
| 159 | class AubioArgumentParser(argparse.ArgumentParser): | ||
| 160 | |||
| 161 | def add_input(self): | ||
| 162 | self.add_argument("source_uri", default=None, nargs='?', | ||
| 163 | help="input sound file to analyse", metavar = "<source_uri>") | ||
| 164 | self.add_argument("-i", "--input", dest = "source_uri2", | ||
| 165 | help="input sound file to analyse", metavar = "<source_uri>") | ||
| 166 | self.add_argument("-r", "--samplerate", | ||
| 167 | metavar = "<freq>", type=int, | ||
| 168 | action="store", dest="samplerate", default=0, | ||
| 169 | help="samplerate at which the file should be represented") | ||
| 170 | |||
| 171 | def add_verbose_help(self): | ||
| 172 | self.add_argument("-v", "--verbose", | ||
| 173 | action="count", dest="verbose", default=1, | ||
| 174 | help="make lots of noise [default]") | ||
| 175 | self.add_argument("-q", "--quiet", | ||
| 176 | action="store_const", dest="verbose", const=0, | ||
| 177 | help="be quiet") | ||
| 178 | |||
| 179 | def add_buf_hop_size(self, buf_size=512, hop_size=256): | ||
| 180 | self.add_buf_size(buf_size=buf_size) | ||
| 181 | self.add_hop_size(hop_size=hop_size) | ||
| 182 | |||
| 183 | def add_buf_size(self, buf_size=512): | ||
| 184 | self.add_argument("-B", "--bufsize", | ||
| 185 | action="store", dest="buf_size", default=buf_size, | ||
| 186 | metavar = "<size>", type=int, | ||
| 187 | help="buffer size [default=%d]" % buf_size) | ||
| 188 | |||
| 189 | def add_hop_size(self, hop_size=256): | ||
| 190 | self.add_argument("-H", "--hopsize", | ||
| 191 | metavar = "<size>", type=int, | ||
| 192 | action="store", dest="hop_size", default=hop_size, | ||
| 193 | help="overlap size [default=%d]" % hop_size) | ||
| 194 | |||
| 195 | def add_method(self, method='default', helpstr='method'): | ||
| 196 | self.add_argument("-m", "--method", | ||
| 197 | metavar = "<method>", type=str, | ||
| 198 | action="store", dest="method", default=method, | ||
| 199 | help="%s [default=%s]" % (helpstr, method)) | ||
| 200 | |||
| 201 | def add_threshold(self, default=None): | ||
| 202 | self.add_argument("-t", "--threshold", | ||
| 203 | metavar = "<threshold>", type=float, | ||
| 204 | action="store", dest="threshold", default=default, | ||
| 205 | help="threshold [default=%s]" % default) | ||
| 206 | |||
| 207 | def add_silence(self): | ||
| 208 | self.add_argument("-s", "--silence", | ||
| 209 | metavar = "<value>", type=float, | ||
| 210 | action="store", dest="silence", default=-70, | ||
| 211 | help="silence threshold") | ||
| 212 | |||
| 213 | def add_release_drop(self): | ||
| 214 | self.add_argument("-d", "--release-drop", | ||
| 215 | metavar = "<value>", type=float, | ||
| 216 | action="store", dest="release_drop", default=10, | ||
| 217 | help="release drop threshold") | ||
| 218 | |||
| 219 | def add_minioi(self, default="12ms"): | ||
| 220 | self.add_argument("-M", "--minioi", | ||
| 221 | metavar = "<value>", type=str, | ||
| 222 | action="store", dest="minioi", default=default, | ||
| 223 | help="minimum Inter-Onset Interval [default=%s]" % default) | ||
| 224 | |||
| 225 | def add_pitch_unit(self, default="Hz"): | ||
| 226 | help_str = "frequency unit, should be one of Hz, midi, bin, cent" | ||
| 227 | help_str += " [default=%s]" % default | ||
| 228 | self.add_argument("-u", "--pitch-unit", | ||
| 229 | metavar = "<value>", type=str, | ||
| 230 | action="store", dest="pitch_unit", default=default, | ||
| 231 | help=help_str) | ||
| 232 | |||
| 233 | def add_time_format(self): | ||
| 234 | helpstr = "select time values output format (samples, ms, seconds)" | ||
| 235 | helpstr += " [default=seconds]" | ||
| 236 | self.add_argument("-T", "--time-format", | ||
| 237 | metavar='format', | ||
| 238 | dest="time_format", | ||
| 239 | default=None, | ||
| 240 | help=helpstr) | ||
| 241 | |||
| 242 | def add_slicer_options(self): | ||
| 243 | self.add_argument("-o", "--output", type = str, | ||
| 244 | metavar = "<outputdir>", | ||
| 245 | action="store", dest="output_directory", default=None, | ||
| 246 | help="specify path where slices of the original file should" | ||
| 247 | " be created") | ||
| 248 | self.add_argument("--cut-until-nsamples", type = int, | ||
| 249 | metavar = "<samples>", | ||
| 250 | action = "store", dest = "cut_until_nsamples", default = None, | ||
| 251 | help="how many extra samples should be added at the end of" | ||
| 252 | " each slice") | ||
| 253 | self.add_argument("--cut-every-nslices", type = int, | ||
| 254 | metavar = "<samples>", | ||
| 255 | action = "store", dest = "cut_every_nslices", default = None, | ||
| 256 | help="how many slices should be groupped together at each cut") | ||
| 257 | self.add_argument("--cut-until-nslices", type = int, | ||
| 258 | metavar = "<slices>", | ||
| 259 | action = "store", dest = "cut_until_nslices", default = None, | ||
| 260 | help="how many extra slices should be added at the end of" | ||
| 261 | " each slice") | ||
| 262 | self.add_argument("--create-first", | ||
| 263 | action = "store_true", dest = "create_first", default = False, | ||
| 264 | help="always include first slice") | ||
| 265 | |||
| 266 | # some utilities | ||
| 267 | |||
| 268 | def samples2seconds(n_frames, samplerate): | ||
| 269 | return "%f\t" % (n_frames / float(samplerate)) | ||
| 270 | |||
| 271 | def samples2milliseconds(n_frames, samplerate): | ||
| 272 | return "%f\t" % (1000. * n_frames / float(samplerate)) | ||
| 273 | |||
| 274 | def samples2samples(n_frames, _samplerate): | ||
| 275 | return "%d\t" % n_frames | ||
| 276 | |||
| 277 | def timefunc(mode): | ||
| 278 | if mode is None or mode == 'seconds' or mode == 's': | ||
| 279 | return samples2seconds | ||
| 280 | elif mode == 'ms' or mode == 'milliseconds': | ||
| 281 | return samples2milliseconds | ||
| 282 | elif mode == 'samples': | ||
| 283 | return samples2samples | ||
| 284 | else: | ||
| 285 | raise ValueError("invalid time format '%s'" % mode) | ||
| 286 | |||
| 287 | # definition of processing classes | ||
| 288 | |||
| 289 | class default_process(object): | ||
| 290 | def __init__(self, args): | ||
| 291 | if 'time_format' in args: | ||
| 292 | self.time2string = timefunc(args.time_format) | ||
| 293 | if args.verbose > 2 and hasattr(self, 'options'): | ||
| 294 | name = type(self).__name__.split('_')[1] | ||
| 295 | optstr = ' '.join(['running', name, 'with options', | ||
| 296 | repr(self.options), '\n']) | ||
| 297 | sys.stderr.write(optstr) | ||
| 298 | def flush(self, frames_read, samplerate): | ||
| 299 | # optionally called at the end of process | ||
| 300 | pass | ||
| 301 | |||
| 302 | def parse_options(self, args, valid_opts): | ||
| 303 | # get any valid options found in a dictionnary of arguments | ||
| 304 | options = {k: v for k, v in vars(args).items() if k in valid_opts} | ||
| 305 | self.options = options | ||
| 306 | |||
| 307 | def remap_pvoc_options(self, options): | ||
| 308 | # FIXME: we need to remap buf_size to win_s, hop_size to hop_s | ||
| 309 | # adjust python/ext/py-phasevoc.c to understand buf_size/hop_size | ||
| 310 | if 'buf_size' in options: | ||
| 311 | options['win_s'] = options['buf_size'] | ||
| 312 | del options['buf_size'] | ||
| 313 | if 'hop_size' in options: | ||
| 314 | options['hop_s'] = options['hop_size'] | ||
| 315 | del options['hop_size'] | ||
| 316 | self.options = options | ||
| 317 | |||
| 318 | class process_onset(default_process): | ||
| 319 | valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] | ||
| 320 | def __init__(self, args): | ||
| 321 | self.parse_options(args, self.valid_opts) | ||
| 322 | self.onset = aubio.onset(**self.options) | ||
| 323 | if args.threshold is not None: | ||
| 324 | self.onset.set_threshold(args.threshold) | ||
| 325 | if args.minioi: | ||
| 326 | if args.minioi.endswith('ms'): | ||
| 327 | self.onset.set_minioi_ms(float(args.minioi[:-2])) | ||
| 328 | elif args.minioi.endswith('s'): | ||
| 329 | self.onset.set_minioi_s(float(args.minioi[:-1])) | ||
| 330 | else: | ||
| 331 | self.onset.set_minioi(int(args.minioi)) | ||
| 332 | if args.silence: | ||
| 333 | self.onset.set_silence(args.silence) | ||
| 334 | super(process_onset, self).__init__(args) | ||
| 335 | def __call__(self, block): | ||
| 336 | return self.onset(block) | ||
| 337 | def repr_res(self, res, _frames_read, samplerate): | ||
| 338 | if res[0] != 0: | ||
| 339 | outstr = self.time2string(self.onset.get_last(), samplerate) | ||
| 340 | sys.stdout.write(outstr + '\n') | ||
| 341 | |||
| 342 | class process_pitch(default_process): | ||
| 343 | valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] | ||
| 344 | def __init__(self, args): | ||
| 345 | self.parse_options(args, self.valid_opts) | ||
| 346 | self.pitch = aubio.pitch(**self.options) | ||
| 347 | if args.pitch_unit is not None: | ||
| 348 | self.pitch.set_unit(args.pitch_unit) | ||
| 349 | if args.threshold is not None: | ||
| 350 | self.pitch.set_tolerance(args.threshold) | ||
| 351 | if args.silence is not None: | ||
| 352 | self.pitch.set_silence(args.silence) | ||
| 353 | super(process_pitch, self).__init__(args) | ||
| 354 | def __call__(self, block): | ||
| 355 | return self.pitch(block) | ||
| 356 | def repr_res(self, res, frames_read, samplerate): | ||
| 357 | fmt_out = self.time2string(frames_read, samplerate) | ||
| 358 | sys.stdout.write(fmt_out + "%.6f\n" % res[0]) | ||
| 359 | |||
| 360 | class process_beat(default_process): | ||
| 361 | valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] | ||
| 362 | def __init__(self, args): | ||
| 363 | self.parse_options(args, self.valid_opts) | ||
| 364 | self.tempo = aubio.tempo(**self.options) | ||
| 365 | super(process_beat, self).__init__(args) | ||
| 366 | def __call__(self, block): | ||
| 367 | return self.tempo(block) | ||
| 368 | def repr_res(self, res, _frames_read, samplerate): | ||
| 369 | if res[0] != 0: | ||
| 370 | outstr = self.time2string(self.tempo.get_last(), samplerate) | ||
| 371 | sys.stdout.write(outstr + '\n') | ||
| 372 | |||
| 373 | class process_tempo(process_beat): | ||
| 374 | def __init__(self, args): | ||
| 375 | super(process_tempo, self).__init__(args) | ||
| 376 | self.beat_locations = [] | ||
| 377 | def repr_res(self, res, _frames_read, samplerate): | ||
| 378 | if res[0] != 0: | ||
| 379 | self.beat_locations.append(self.tempo.get_last_s()) | ||
| 380 | def flush(self, frames_read, samplerate): | ||
| 381 | import numpy as np | ||
| 382 | if len(self.beat_locations) < 2: | ||
| 383 | outstr = "unknown bpm" | ||
| 384 | else: | ||
| 385 | bpms = 60. / np.diff(self.beat_locations) | ||
| 386 | median_bpm = np.mean(bpms) | ||
| 387 | if len(self.beat_locations) < 10: | ||
| 388 | outstr = "%.2f bpm (uncertain)" % median_bpm | ||
| 389 | else: | ||
| 390 | outstr = "%.2f bpm" % median_bpm | ||
| 391 | sys.stdout.write(outstr + '\n') | ||
| 392 | |||
| 393 | class process_notes(default_process): | ||
| 394 | valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] | ||
| 395 | def __init__(self, args): | ||
| 396 | self.parse_options(args, self.valid_opts) | ||
| 397 | self.notes = aubio.notes(**self.options) | ||
| 398 | if args.silence is not None: | ||
| 399 | self.notes.set_silence(args.silence) | ||
| 400 | if args.release_drop is not None: | ||
| 401 | self.notes.set_release_drop(args.release_drop) | ||
| 402 | super(process_notes, self).__init__(args) | ||
| 403 | def __call__(self, block): | ||
| 404 | return self.notes(block) | ||
| 405 | def repr_res(self, res, frames_read, samplerate): | ||
| 406 | if res[2] != 0: # note off | ||
| 407 | fmt_out = self.time2string(frames_read, samplerate) | ||
| 408 | sys.stdout.write(fmt_out + '\n') | ||
| 409 | if res[0] != 0: # note on | ||
| 410 | lastmidi = res[0] | ||
| 411 | fmt_out = "%f\t" % lastmidi | ||
| 412 | fmt_out += self.time2string(frames_read, samplerate) | ||
| 413 | sys.stdout.write(fmt_out) # + '\t') | ||
| 414 | def flush(self, frames_read, samplerate): | ||
| 415 | eof = self.time2string(frames_read, samplerate) | ||
| 416 | sys.stdout.write(eof + '\n') | ||
| 417 | |||
| 418 | class process_mfcc(default_process): | ||
| 419 | def __init__(self, args): | ||
| 420 | valid_opts1 = ['hop_size', 'buf_size'] | ||
| 421 | self.parse_options(args, valid_opts1) | ||
| 422 | self.remap_pvoc_options(self.options) | ||
| 423 | self.pv = aubio.pvoc(**self.options) | ||
| 424 | |||
| 425 | valid_opts2 = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate'] | ||
| 426 | self.parse_options(args, valid_opts2) | ||
| 427 | self.mfcc = aubio.mfcc(**self.options) | ||
| 428 | |||
| 429 | # remember all options | ||
| 430 | self.parse_options(args, list(set(valid_opts1 + valid_opts2))) | ||
| 431 | |||
| 432 | super(process_mfcc, self).__init__(args) | ||
| 433 | |||
| 434 | def __call__(self, block): | ||
| 435 | fftgrain = self.pv(block) | ||
| 436 | return self.mfcc(fftgrain) | ||
| 437 | def repr_res(self, res, frames_read, samplerate): | ||
| 438 | fmt_out = self.time2string(frames_read, samplerate) | ||
| 439 | fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()]) | ||
| 440 | sys.stdout.write(fmt_out + '\n') | ||
| 441 | |||
| 442 | class process_melbands(default_process): | ||
| 443 | def __init__(self, args): | ||
| 444 | self.args = args | ||
| 445 | valid_opts = ['hop_size', 'buf_size'] | ||
| 446 | self.parse_options(args, valid_opts) | ||
| 447 | self.remap_pvoc_options(self.options) | ||
| 448 | self.pv = aubio.pvoc(**self.options) | ||
| 449 | |||
| 450 | valid_opts = ['buf_size', 'n_filters'] | ||
| 451 | self.parse_options(args, valid_opts) | ||
| 452 | self.remap_pvoc_options(self.options) | ||
| 453 | self.filterbank = aubio.filterbank(**self.options) | ||
| 454 | self.filterbank.set_mel_coeffs_slaney(args.samplerate) | ||
| 455 | |||
| 456 | super(process_melbands, self).__init__(args) | ||
| 457 | def __call__(self, block): | ||
| 458 | fftgrain = self.pv(block) | ||
| 459 | return self.filterbank(fftgrain) | ||
| 460 | def repr_res(self, res, frames_read, samplerate): | ||
| 461 | fmt_out = self.time2string(frames_read, samplerate) | ||
| 462 | fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()]) | ||
| 463 | sys.stdout.write(fmt_out + '\n') | ||
| 464 | |||
| 465 | class process_quiet(default_process): | ||
| 466 | def __init__(self, args): | ||
| 467 | self.args = args | ||
| 468 | valid_opts = ['hop_size', 'silence'] | ||
| 469 | self.parse_options(args, valid_opts) | ||
| 470 | self.wassilence = 1 | ||
| 471 | |||
| 472 | if args.silence is not None: | ||
| 473 | self.silence = args.silence | ||
| 474 | super(process_quiet, self).__init__(args) | ||
| 475 | |||
| 476 | def __call__(self, block): | ||
| 477 | if aubio.silence_detection(block, self.silence) == 1: | ||
| 478 | if self.wassilence != 1: | ||
| 479 | self.wassilence = 1 | ||
| 480 | return 2 # newly found silence | ||
| 481 | return 1 # silence again | ||
| 482 | else: | ||
| 483 | if self.wassilence != 0: | ||
| 484 | self.wassilence = 0 | ||
| 485 | return -1 # newly found noise | ||
| 486 | return 0 # noise again | ||
| 487 | |||
| 488 | def repr_res(self, res, frames_read, samplerate): | ||
| 489 | fmt_out = None | ||
| 490 | if res == -1: | ||
| 491 | fmt_out = "NOISY: " | ||
| 492 | if res == 2: | ||
| 493 | fmt_out = "QUIET: " | ||
| 494 | if fmt_out is not None: | ||
| 495 | fmt_out += self.time2string(frames_read, samplerate) | ||
| 496 | sys.stdout.write(fmt_out + '\n') | ||
| 497 | |||
| 498 | class process_cut(process_onset): | ||
| 499 | def __init__(self, args): | ||
| 500 | super(process_cut, self).__init__(args) | ||
| 501 | self.slices = [] | ||
| 502 | self.options = args | ||
| 503 | |||
| 504 | def __call__(self, block): | ||
| 505 | ret = super(process_cut, self).__call__(block) | ||
| 506 | if ret: | ||
| 507 | self.slices.append(self.onset.get_last()) | ||
| 508 | return ret | ||
| 509 | |||
| 510 | def flush(self, frames_read, samplerate): | ||
| 511 | _cut_slice(self.options, self.slices) | ||
| 512 | duration = float(frames_read) / float(samplerate) | ||
| 513 | base_info = '%(source_file)s' % \ | ||
| 514 | {'source_file': self.options.source_uri} | ||
| 515 | base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \ | ||
| 516 | {'duration': duration, 'samplerate': samplerate} | ||
| 517 | info = "created %d slices from " % len(self.slices) | ||
| 518 | info += base_info | ||
| 519 | sys.stderr.write(info) | ||
| 520 | |||
| 521 | def _cut_slice(options, timestamps): | ||
| 522 | # cutting pass | ||
| 523 | nstamps = len(timestamps) | ||
| 524 | if nstamps > 0: | ||
| 525 | # generate output files | ||
| 526 | timestamps_end = None | ||
| 527 | if options.cut_every_nslices: | ||
| 528 | timestamps = timestamps[::options.cut_every_nslices] | ||
| 529 | nstamps = len(timestamps) | ||
| 530 | if options.cut_until_nslices and options.cut_until_nsamples: | ||
| 531 | msg = "using cut_until_nslices, but cut_until_nsamples is set" | ||
| 532 | warnings.warn(msg) | ||
| 533 | if options.cut_until_nsamples: | ||
| 534 | lag = options.cut_until_nsamples | ||
| 535 | timestamps_end = [t + lag for t in timestamps[1:]] | ||
| 536 | timestamps_end += [1e120] | ||
| 537 | if options.cut_until_nslices: | ||
| 538 | slice_lag = options.cut_until_nslices | ||
| 539 | timestamps_end = [t for t in timestamps[1 + slice_lag:]] | ||
| 540 | timestamps_end += [1e120] * (options.cut_until_nslices + 1) | ||
| 541 | aubio.slice_source_at_stamps(options.source_uri, | ||
| 542 | timestamps, timestamps_end = timestamps_end, | ||
| 543 | output_dir = options.output_directory, | ||
| 544 | samplerate = options.samplerate, | ||
| 545 | create_first = options.create_first) | ||
| 546 | |||
| 547 | def main(): | ||
| 548 | parser = aubio_parser() | ||
| 549 | if sys.version_info[0] != 3: | ||
| 550 | # on py2, create a dummy ArgumentParser to workaround the | ||
| 551 | # optional subcommand issue. See https://bugs.python.org/issue9253 | ||
| 552 | # This ensures that: | ||
| 553 | # - version string is shown when only '-V' is passed | ||
| 554 | # - help is printed if '-V' is passed with any other argument | ||
| 555 | # - any other argument get forwarded to the real parser | ||
| 556 | parser_root = argparse.ArgumentParser(add_help=False) | ||
| 557 | parser_root.add_argument('-V', '--version', help="show version", | ||
| 558 | action="store_true", dest="show_version") | ||
| 559 | args, extras = parser_root.parse_known_args() | ||
| 560 | if not args.show_version: # no -V, forward to parser | ||
| 561 | args = parser.parse_args(extras, namespace=args) | ||
| 562 | elif len(extras) != 0: # -V with other arguments, print help | ||
| 563 | parser.print_help() | ||
| 564 | sys.exit(1) | ||
| 565 | else: # in py3, we can simply use parser directly | ||
| 566 | args = parser.parse_args() | ||
| 567 | if 'show_version' in args and args.show_version: | ||
| 568 | sys.stdout.write('aubio version ' + aubio.version + '\n') | ||
| 569 | sys.exit(0) | ||
| 570 | elif 'verbose' in args and args.verbose > 3: | ||
| 571 | sys.stderr.write('aubio version ' + aubio.version + '\n') | ||
| 572 | if 'command' not in args or args.command is None \ | ||
| 573 | or args.command in ['help']: | ||
| 574 | # no command given, print help and return 1 | ||
| 575 | parser.print_help() | ||
| 576 | if args.command and args.command in ['help']: | ||
| 577 | sys.exit(0) | ||
| 578 | else: | ||
| 579 | sys.exit(1) | ||
| 580 | elif not args.source_uri and not args.source_uri2: | ||
| 581 | sys.stderr.write("Error: a source is required\n") | ||
| 582 | parser.print_help() | ||
| 583 | sys.exit(1) | ||
| 584 | elif args.source_uri2 is not None: | ||
| 585 | args.source_uri = args.source_uri2 | ||
| 586 | try: | ||
| 587 | # open source_uri | ||
| 588 | with aubio.source(args.source_uri, hop_size=args.hop_size, | ||
| 589 | samplerate=args.samplerate) as a_source: | ||
| 590 | # always update args.samplerate to native samplerate, in case | ||
| 591 | # source was opened with args.samplerate=0 | ||
| 592 | args.samplerate = a_source.samplerate | ||
| 593 | # create the processor for this subcommand | ||
| 594 | processor = args.process(args) | ||
| 595 | frames_read = 0 | ||
| 596 | while True: | ||
| 597 | # read new block from source | ||
| 598 | block, read = a_source() | ||
| 599 | # execute processor on this block | ||
| 600 | res = processor(block) | ||
| 601 | # print results for this block | ||
| 602 | if args.verbose > 0: | ||
| 603 | processor.repr_res(res, frames_read, a_source.samplerate) | ||
| 604 | # increment total number of frames read | ||
| 605 | frames_read += read | ||
| 606 | # exit loop at end of file | ||
| 607 | if read < a_source.hop_size: | ||
| 608 | break | ||
| 609 | # flush the processor if needed | ||
| 610 | processor.flush(frames_read, a_source.samplerate) | ||
| 611 | if args.verbose > 1: | ||
| 612 | fmt_string = "read {:.2f}s" | ||
| 613 | fmt_string += " ({:d} samples in {:d} blocks of {:d})" | ||
| 614 | fmt_string += " from {:s} at {:d}Hz\n" | ||
| 615 | sys.stderr.write(fmt_string.format( | ||
| 616 | frames_read / float(a_source.samplerate), | ||
| 617 | frames_read, | ||
| 618 | frames_read // a_source.hop_size + 1, | ||
| 619 | a_source.hop_size, | ||
| 620 | a_source.uri, | ||
| 621 | a_source.samplerate)) | ||
| 622 | except KeyboardInterrupt: | ||
| 623 | sys.exit(1) | ||
| @@ -0,0 +1,163 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """ this file was written by Paul Brossier | ||
| 4 | it is released under the GNU/GPL license. | ||
| 5 | """ | ||
| 6 | |||
| 7 | import sys | ||
| 8 | from aubio.cmd import AubioArgumentParser, _cut_slice | ||
| 9 | |||
| 10 | def aubio_cut_parser(): | ||
| 11 | parser = AubioArgumentParser() | ||
| 12 | parser.add_input() | ||
| 13 | parser.add_argument("-O", "--onset-method", | ||
| 14 | action="store", dest="onset_method", default='default', | ||
| 15 | metavar = "<onset_method>", | ||
| 16 | help="onset detection method [default=default] \ | ||
| 17 | complexdomain|hfc|phase|specdiff|energy|kl|mkl") | ||
| 18 | # cutting methods | ||
| 19 | parser.add_argument("-b", "--beat", | ||
| 20 | action="store_true", dest="beat", default=False, | ||
| 21 | help="slice at beat locations") | ||
| 22 | """ | ||
| 23 | parser.add_argument("-S", "--silencecut", | ||
| 24 | action="store_true", dest="silencecut", default=False, | ||
| 25 | help="use silence locations") | ||
| 26 | parser.add_argument("-s", "--silence", | ||
| 27 | metavar = "<value>", | ||
| 28 | action="store", dest="silence", default=-70, | ||
| 29 | help="silence threshold [default=-70]") | ||
| 30 | """ | ||
| 31 | # algorithm parameters | ||
| 32 | parser.add_buf_hop_size() | ||
| 33 | parser.add_argument("-t", "--threshold", "--onset-threshold", | ||
| 34 | metavar = "<threshold>", type=float, | ||
| 35 | action="store", dest="threshold", default=0.3, | ||
| 36 | help="onset peak picking threshold [default=0.3]") | ||
| 37 | parser.add_argument("-c", "--cut", | ||
| 38 | action="store_true", dest="cut", default=False, | ||
| 39 | help="cut input sound file at detected labels") | ||
| 40 | parser.add_minioi() | ||
| 41 | |||
| 42 | """ | ||
| 43 | parser.add_argument("-D", "--delay", | ||
| 44 | action = "store", dest = "delay", type = float, | ||
| 45 | metavar = "<seconds>", default=0, | ||
| 46 | help="number of seconds to take back [default=system]\ | ||
| 47 | default system delay is 3*hopsize/samplerate") | ||
| 48 | parser.add_argument("-C", "--dcthreshold", | ||
| 49 | metavar = "<value>", | ||
| 50 | action="store", dest="dcthreshold", default=1., | ||
| 51 | help="onset peak picking DC component [default=1.]") | ||
| 52 | parser.add_argument("-L", "--localmin", | ||
| 53 | action="store_true", dest="localmin", default=False, | ||
| 54 | help="use local minima after peak detection") | ||
| 55 | parser.add_argument("-d", "--derivate", | ||
| 56 | action="store_true", dest="derivate", default=False, | ||
| 57 | help="derivate onset detection function") | ||
| 58 | parser.add_argument("-z", "--zerocross", | ||
| 59 | metavar = "<value>", | ||
| 60 | action="store", dest="zerothres", default=0.008, | ||
| 61 | help="zero-crossing threshold for slicing [default=0.00008]") | ||
| 62 | # plotting functions | ||
| 63 | parser.add_argument("-p", "--plot", | ||
| 64 | action="store_true", dest="plot", default=False, | ||
| 65 | help="draw plot") | ||
| 66 | parser.add_argument("-x", "--xsize", | ||
| 67 | metavar = "<size>", | ||
| 68 | action="store", dest="xsize", default=1., | ||
| 69 | type=float, help="define xsize for plot") | ||
| 70 | parser.add_argument("-y", "--ysize", | ||
| 71 | metavar = "<size>", | ||
| 72 | action="store", dest="ysize", default=1., | ||
| 73 | type=float, help="define ysize for plot") | ||
| 74 | parser.add_argument("-f", "--function", | ||
| 75 | action="store_true", dest="func", default=False, | ||
| 76 | help="print detection function") | ||
| 77 | parser.add_argument("-n", "--no-onsets", | ||
| 78 | action="store_true", dest="nplot", default=False, | ||
| 79 | help="do not plot detected onsets") | ||
| 80 | parser.add_argument("-O", "--outplot", | ||
| 81 | metavar = "<output_image>", | ||
| 82 | action="store", dest="outplot", default=None, | ||
| 83 | help="save plot to output.{ps,png}") | ||
| 84 | parser.add_argument("-F", "--spectrogram", | ||
| 85 | action="store_true", dest="spectro", default=False, | ||
| 86 | help="add spectrogram to the plot") | ||
| 87 | """ | ||
| 88 | parser.add_slicer_options() | ||
| 89 | parser.add_verbose_help() | ||
| 90 | return parser | ||
| 91 | |||
| 92 | |||
| 93 | def _cut_analyze(options): | ||
| 94 | hopsize = options.hop_size | ||
| 95 | bufsize = options.buf_size | ||
| 96 | samplerate = options.samplerate | ||
| 97 | source_uri = options.source_uri | ||
| 98 | |||
| 99 | # analyze pass | ||
| 100 | from aubio import onset, tempo, source | ||
| 101 | |||
| 102 | s = source(source_uri, samplerate, hopsize) | ||
| 103 | if samplerate == 0: | ||
| 104 | samplerate = s.samplerate | ||
| 105 | options.samplerate = samplerate | ||
| 106 | |||
| 107 | if options.beat: | ||
| 108 | o = tempo(options.onset_method, bufsize, hopsize, | ||
| 109 | samplerate=samplerate) | ||
| 110 | else: | ||
| 111 | o = onset(options.onset_method, bufsize, hopsize, | ||
| 112 | samplerate=samplerate) | ||
| 113 | if options.minioi: | ||
| 114 | if options.minioi.endswith('ms'): | ||
| 115 | o.set_minioi_ms(int(options.minioi[:-2])) | ||
| 116 | elif options.minioi.endswith('s'): | ||
| 117 | o.set_minioi_s(int(options.minioi[:-1])) | ||
| 118 | else: | ||
| 119 | o.set_minioi(int(options.minioi)) | ||
| 120 | o.set_threshold(options.threshold) | ||
| 121 | |||
| 122 | timestamps = [] | ||
| 123 | total_frames = 0 | ||
| 124 | while True: | ||
| 125 | samples, read = s() | ||
| 126 | if o(samples): | ||
| 127 | timestamps.append(o.get_last()) | ||
| 128 | if options.verbose: | ||
| 129 | print("%.4f" % o.get_last_s()) | ||
| 130 | total_frames += read | ||
| 131 | if read < hopsize: | ||
| 132 | break | ||
| 133 | del s | ||
| 134 | return timestamps, total_frames | ||
| 135 | |||
| 136 | def main(): | ||
| 137 | parser = aubio_cut_parser() | ||
| 138 | options = parser.parse_args() | ||
| 139 | if not options.source_uri and not options.source_uri2: | ||
| 140 | sys.stderr.write("Error: no file name given\n") | ||
| 141 | parser.print_help() | ||
| 142 | sys.exit(1) | ||
| 143 | elif options.source_uri2 is not None: | ||
| 144 | options.source_uri = options.source_uri2 | ||
| 145 | |||
| 146 | # analysis | ||
| 147 | timestamps, total_frames = _cut_analyze(options) | ||
| 148 | |||
| 149 | # print some info | ||
| 150 | duration = float(total_frames) / float(options.samplerate) | ||
| 151 | base_info = '%(source_uri)s' % {'source_uri': options.source_uri} | ||
| 152 | base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \ | ||
| 153 | {'duration': duration, 'samplerate': options.samplerate} | ||
| 154 | |||
| 155 | info = "found %d timestamps in " % len(timestamps) | ||
| 156 | info += base_info | ||
| 157 | sys.stderr.write(info) | ||
| 158 | |||
| 159 | if options.cut: | ||
| 160 | _cut_slice(options, timestamps) | ||
| 161 | info = "created %d slices from " % len(timestamps) | ||
| 162 | info += base_info | ||
| 163 | sys.stderr.write(info) | ||
| @@ -1,9 +1,11 @@ | |||
| 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
| 2 | """ utilities to convert midi note number to and from note names """ | 2 | """ utilities to convert midi note number to and from note names """ |
| 3 | 3 | ||
| 4 | __all__ = ['note2midi', 'midi2note', 'freq2note'] | ||
| 5 | |||
| 6 | import sys | 4 | import sys |
| 5 | from ._aubio import freqtomidi, miditofreq | ||
| 6 | |||
| 7 | __all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq'] | ||
| 8 | |||
| 7 | py3 = sys.version_info[0] == 3 | 9 | py3 = sys.version_info[0] == 3 |
| 8 | if py3: | 10 | if py3: |
| 9 | str_instances = str | 11 | str_instances = str |
| @@ -12,18 +14,65 @@ else: · 2 unmodified lines | |||
| 12 | str_instances = (str, unicode) | 14 | str_instances = (str, unicode) |
| 13 | int_instances = (int, long) | 15 | int_instances = (int, long) |
| 14 | 16 | ||
| 17 | |||
| 15 | def note2midi(note): | 18 | def note2midi(note): |
| 16 | " convert note name to midi note number, e.g. [C-1, G9] -> [0, 127] " | 19 | """Convert note name to midi note number. |
| 17 | _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11} | 20 | |
| 18 | _valid_modifiers = {None: 0, u'♮': 0, '#': +1, u'♯': +1, u'\udd2a': +2, | 21 | Input string `note` should be composed of one note root |
| 19 | 'b': -1, u'♭': -1, u'\ufffd': -2} | 22 | and one octave, with optionally one modifier in between. |
| 23 | |||
| 24 | List of valid components: | ||
| 25 | |||
| 26 | - note roots: `C`, `D`, `E`, `F`, `G`, `A`, `B`, | ||
| 27 | - modifiers: `b`, `#`, as well as unicode characters | ||
| 28 | `𝄫`, `♭`, `♮`, `♯` and `𝄪`, | ||
| 29 | - octave numbers: `-1` -> `11`. | ||
| 30 | |||
| 31 | Parameters | ||
| 32 | ---------- | ||
| 33 | note : str | ||
| 34 | note name | ||
| 35 | |||
| 36 | Returns | ||
| 37 | ------- | ||
| 38 | int | ||
| 39 | corresponding midi note number | ||
| 40 | |||
| 41 | Examples | ||
| 42 | -------- | ||
| 43 | >>> aubio.note2midi('C#4') | ||
| 44 | 61 | ||
| 45 | >>> aubio.note2midi('B♭5') | ||
| 46 | 82 | ||
| 47 | |||
| 48 | Raises | ||
| 49 | ------ | ||
| 50 | TypeError | ||
| 51 | If `note` was not a string. | ||
| 52 | ValueError | ||
| 53 | If an error was found while converting `note`. | ||
| 54 | |||
| 55 | See Also | ||
| 56 | -------- | ||
| 57 | midi2note, freqtomidi, miditofreq | ||
| 58 | """ | ||
| 59 | _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, | ||
| 60 | 'A': 9, 'B': 11} | ||
| 61 | _valid_modifiers = { | ||
| 62 | u'𝄫': -2, # double flat | ||
| 63 | u'♭': -1, 'b': -1, '\u266d': -1, # simple flat | ||
| 64 | u'♮': 0, '\u266e': 0, None: 0, # natural | ||
| 65 | '#': +1, u'♯': +1, '\u266f': +1, # sharp | ||
| 66 | u'𝄪': +2, # double sharp | ||
| 67 | } | ||
| 20 | _valid_octaves = range(-1, 10) | 68 | _valid_octaves = range(-1, 10) |
| 21 | if not isinstance(note, str_instances): | 69 | if not isinstance(note, str_instances): |
| 22 | raise TypeError("a string is required, got %s (%s)" % (note, str(type(note)))) | 70 | msg = "a string is required, got {:s} ({:s})" |
| 71 | raise TypeError(msg.format(str(type(note)), repr(note))) | ||
| 23 | if len(note) not in range(2, 5): | 72 | if len(note) not in range(2, 5): |
| 24 | raise ValueError("string of 2 to 4 characters expected, got %d (%s)" \ | 73 | msg = "string of 2 to 4 characters expected, got {:d} ({:s})" |
| 25 | % (len(note), note)) | 74 | raise ValueError(msg.format(len(note), note)) |
| 26 | notename, modifier, octave = [None]*3 | 75 | notename, modifier, octave = [None] * 3 |
| 27 | 76 | ||
| 28 | if len(note) == 4: | 77 | if len(note) == 4: |
| 29 | notename, modifier, octave_sign, octave = note | 78 | notename, modifier, octave_sign, octave = note |
| @@ -46,21 +95,97 @@ def note2midi(note): · 16 unmodified lines | |||
| 46 | if octave not in _valid_octaves: | 95 | if octave not in _valid_octaves: |
| 47 | raise ValueError("%s is not a valid octave" % octave) | 96 | raise ValueError("%s is not a valid octave" % octave) |
| 48 | 97 | ||
| 49 | midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier] | 98 | midi = (octave + 1) * 12 + _valid_notenames[notename] \ |
| 99 | + _valid_modifiers[modifier] | ||
| 50 | if midi > 127: | 100 | if midi > 127: |
| 51 | raise ValueError("%s is outside of the range C-2 to G8" % note) | 101 | raise ValueError("%s is outside of the range C-2 to G8" % note) |
| 52 | return midi | 102 | return midi |
| 53 | 103 | ||
| 104 | |||
| 54 | def midi2note(midi): | 105 | def midi2note(midi): |
| 55 | " convert midi note number to note name, e.g. [0, 127] -> [C-1, G9] " | 106 | """Convert midi note number to note name. |
| 107 | |||
| 108 | Parameters | ||
| 109 | ---------- | ||
| 110 | midi : int [0, 128] | ||
| 111 | input midi note number | ||
| 112 | |||
| 113 | Returns | ||
| 114 | ------- | ||
| 115 | str | ||
| 116 | note name | ||
| 117 | |||
| 118 | Examples | ||
| 119 | -------- | ||
| 120 | >>> aubio.midi2note(70) | ||
| 121 | 'A#4' | ||
| 122 | >>> aubio.midi2note(59) | ||
| 123 | 'B3' | ||
| 124 | |||
| 125 | Raises | ||
| 126 | ------ | ||
| 127 | TypeError | ||
| 128 | If `midi` was not an integer. | ||
| 129 | ValueError | ||
| 130 | If `midi` is out of the range `[0, 128]`. | ||
| 131 | |||
| 132 | See Also | ||
| 133 | -------- | ||
| 134 | note2midi, miditofreq, freqtomidi | ||
| 135 | """ | ||
| 56 | if not isinstance(midi, int_instances): | 136 | if not isinstance(midi, int_instances): |
| 57 | raise TypeError("an integer is required, got %s" % midi) | 137 | raise TypeError("an integer is required, got %s" % midi) |
| 58 | if midi not in range(0, 128): | 138 | if midi not in range(0, 128): |
| 59 | raise ValueError("an integer between 0 and 127 is excepted, got %d" % midi) | 139 | msg = "an integer between 0 and 127 is excepted, got {:d}" |
| 60 | _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | 140 | raise ValueError(msg.format(midi)) |
| 141 | _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', | ||
| 142 | 'A', 'A#', 'B'] | ||
| 61 | return _valid_notenames[midi % 12] + str(int(midi / 12) - 1) | 143 | return _valid_notenames[midi % 12] + str(int(midi / 12) - 1) |
| 62 | 144 | ||
| 145 | |||
| 63 | def freq2note(freq): | 146 | def freq2note(freq): |
| 64 | " convert frequency in Hz to nearest note name, e.g. [0, 22050.] -> [C-1, G9] " | 147 | """Convert frequency in Hz to nearest note name. |
| 65 | from aubio import freqtomidi | 148 | |
| 66 | return midi2note(int(freqtomidi(freq))) | 149 | Parameters |
| 150 | ---------- | ||
| 151 | freq : float [0, 23000[ | ||
| 152 | input frequency, in Hz | ||
| 153 | |||
| 154 | Returns | ||
| 155 | ------- | ||
| 156 | str | ||
| 157 | name of the nearest note | ||
| 158 | |||
| 159 | Example | ||
| 160 | ------- | ||
| 161 | >>> aubio.freq2note(440) | ||
| 162 | 'A4' | ||
| 163 | >>> aubio.freq2note(220.1) | ||
| 164 | 'A3' | ||
| 165 | """ | ||
| 166 | nearest_note = int(freqtomidi(freq) + .5) | ||
| 167 | return midi2note(nearest_note) | ||
| 168 | |||
| 169 | |||
| 170 | def note2freq(note): | ||
| 171 | """Convert note name to corresponding frequency, in Hz. | ||
| 172 | |||
| 173 | Parameters | ||
| 174 | ---------- | ||
| 175 | note : str | ||
| 176 | input note name | ||
| 177 | |||
| 178 | Returns | ||
| 179 | ------- | ||
| 180 | freq : float [0, 23000[ | ||
| 181 | frequency, in Hz | ||
| 182 | |||
| 183 | Example | ||
| 184 | ------- | ||
| 185 | >>> aubio.note2freq('A4') | ||
| 186 | 440 | ||
| 187 | >>> aubio.note2freq('A3') | ||
| 188 | 220.1 | ||
| 189 | """ | ||
| 190 | midi = note2midi(note) | ||
| 191 | return miditofreq(midi) | ||
| @@ -5,26 +5,91 @@ from aubio import source, sink · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | _max_timestamp = 1e120 | 6 | _max_timestamp = 1e120 |
| 7 | 7 | ||
| 8 | |||
| 8 | def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, | 9 | def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, |
| 9 | output_dir=None, samplerate=0, hopsize=256): | 10 | output_dir=None, samplerate=0, hopsize=256, |
| 10 | """ slice a sound file at given timestamps """ | 11 | create_first=False): |
| 12 | """Slice a sound file at given timestamps. | ||
| 13 | |||
| 14 | This function reads `source_file` and creates slices, new smaller | ||
| 15 | files each starting at `t` in `timestamps`, a list of integer | ||
| 16 | corresponding to time locations in `source_file`, in samples. | ||
| 17 | |||
| 18 | If `timestamps_end` is unspecified, the slices will end at | ||
| 19 | `timestamps_end[n] = timestamps[n+1]-1`, or the end of file. | ||
| 20 | Otherwise, `timestamps_end` should be a list with the same length | ||
| 21 | as `timestamps` containing the locations of the end of each slice. | ||
| 22 | |||
| 23 | If `output_dir` is unspecified, the new slices will be written in | ||
| 24 | the current directory. If `output_dir` is a string, new slices | ||
| 25 | will be written in `output_dir`, after creating the directory if | ||
| 26 | required. | ||
| 27 | |||
| 28 | The default `samplerate` is 0, meaning the original sampling rate | ||
| 29 | of `source_file` will be used. When using a sampling rate | ||
| 30 | different to the one of the original files, `timestamps` and | ||
| 31 | `timestamps_end` should be expressed in the re-sampled signal. | ||
| 32 | |||
| 33 | The `hopsize` parameter simply tells :class:`source` to use this | ||
| 34 | hopsize and does not change the output slices. | ||
| 35 | |||
| 36 | If `create_first` is True and `timestamps` does not start with `0`, the | ||
| 37 | first slice from `0` to `timestamps[0] - 1` will be automatically added. | ||
| 38 | |||
| 39 | Parameters | ||
| 40 | ---------- | ||
| 41 | source_file : str | ||
| 42 | path of the resource to slice | ||
| 43 | timestamps : :obj:`list` of :obj:`int` | ||
| 44 | time stamps at which to slice, in samples | ||
| 45 | timestamps_end : :obj:`list` of :obj:`int` (optional) | ||
| 46 | time stamps at which to end the slices | ||
| 47 | output_dir : str (optional) | ||
| 48 | output directory to write the slices to | ||
| 49 | samplerate : int (optional) | ||
| 50 | samplerate to read the file at | ||
| 51 | hopsize : int (optional) | ||
| 52 | number of samples read from source per iteration | ||
| 53 | create_first : bool (optional) | ||
| 54 | always create the slice at the start of the file | ||
| 55 | |||
| 56 | Examples | ||
| 57 | -------- | ||
| 58 | Create two slices: the first slice starts at the beginning of the | ||
| 59 | input file `loop.wav` and lasts exactly one second, starting at | ||
| 60 | sample `0` and ending at sample `44099`; the second slice starts | ||
| 61 | at sample `44100` and lasts until the end of the input file: | ||
| 62 | |||
| 63 | >>> aubio.slice_source_at_stamps('loop.wav', [0, 44100]) | ||
| 64 | |||
| 65 | Create one slice, from 1 second to 2 seconds: | ||
| 66 | |||
| 67 | >>> aubio.slice_source_at_stamps('loop.wav', [44100], [44100 * 2 - 1]) | ||
| 68 | |||
| 69 | Notes | ||
| 70 | ----- | ||
| 71 | Slices may be overlapping. If `timestamps_end` is `1` element | ||
| 72 | shorter than `timestamps`, the last slice will end at the end of | ||
| 73 | the file. | ||
| 74 | """ | ||
| 11 | 75 | ||
| 12 | if timestamps is None or len(timestamps) == 0: | 76 | if not timestamps: |
| 13 | raise ValueError("no timestamps given") | 77 | raise ValueError("no timestamps given") |
| 14 | 78 | ||
| 15 | if timestamps[0] != 0: | 79 | if timestamps[0] != 0 and create_first: |
| 16 | timestamps = [0] + timestamps | 80 | timestamps = [0] + timestamps |
| 17 | if timestamps_end is not None: | 81 | if timestamps_end is not None: |
| 18 | timestamps_end = [timestamps[1] - 1] + timestamps_end | 82 | timestamps_end = [timestamps[1] - 1] + timestamps_end |
| 19 | 83 | ||
| 20 | if timestamps_end is not None: | 84 | if timestamps_end is not None: |
| 21 | if len(timestamps_end) != len(timestamps): | 85 | if len(timestamps_end) == len(timestamps) - 1: |
| 86 | timestamps_end = timestamps_end + [_max_timestamp] | ||
| 87 | elif len(timestamps_end) != len(timestamps): | ||
| 22 | raise ValueError("len(timestamps_end) != len(timestamps)") | 88 | raise ValueError("len(timestamps_end) != len(timestamps)") |
| 23 | else: | 89 | else: |
| 24 | timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp] | 90 | timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp] |
| 25 | 91 | ||
| 26 | regions = list(zip(timestamps, timestamps_end)) | 92 | regions = list(zip(timestamps, timestamps_end)) |
| 27 | #print regions | ||
| 28 | 93 | ||
| 29 | source_base_name, _ = os.path.splitext(os.path.basename(source_file)) | 94 | source_base_name, _ = os.path.splitext(os.path.basename(source_file)) |
| 30 | if output_dir is not None: | 95 | if output_dir is not None: |
| @@ -32,8 +97,8 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 1 unmodified line | |||
| 32 | os.makedirs(output_dir) | 97 | os.makedirs(output_dir) |
| 33 | source_base_name = os.path.join(output_dir, source_base_name) | 98 | source_base_name = os.path.join(output_dir, source_base_name) |
| 34 | 99 | ||
| 35 | def new_sink_name(source_base_name, timestamp, samplerate): | 100 | def _new_sink_name(source_base_name, timestamp, samplerate): |
| 36 | """ create a sink based on a timestamp in samples, converted in seconds """ | 101 | # create name based on a timestamp in samples, converted in seconds |
| 37 | timestamp_seconds = timestamp / float(samplerate) | 102 | timestamp_seconds = timestamp / float(samplerate) |
| 38 | return source_base_name + "_%011.6f" % timestamp_seconds + '.wav' | 103 | return source_base_name + "_%011.6f" % timestamp_seconds + '.wav' |
| 39 | 104 | ||
| @@ -48,16 +113,17 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 8 unmodified lines | |||
| 48 | # get hopsize new samples from source | 113 | # get hopsize new samples from source |
| 49 | vec, read = _source.do_multi() | 114 | vec, read = _source.do_multi() |
| 50 | # if the total number of frames read will exceed the next region start | 115 | # if the total number of frames read will exceed the next region start |
| 51 | if len(regions) and total_frames + read >= regions[0][0]: | 116 | while regions and total_frames + read >= regions[0][0]: |
| 52 | #print "getting", regions[0], "at", total_frames | ||
| 53 | # get next region | 117 | # get next region |
| 54 | start_stamp, end_stamp = regions.pop(0) | 118 | start_stamp, end_stamp = regions.pop(0) |
| 55 | # create a name for the sink | 119 | # create a name for the sink |
| 56 | new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate) | 120 | new_sink_path = _new_sink_name(source_base_name, start_stamp, |
| 121 | samplerate) | ||
| 57 | # create its sink | 122 | # create its sink |
| 58 | _sink = sink(new_sink_path, samplerate, _source.channels) | 123 | _sink = sink(new_sink_path, samplerate, _source.channels) |
| 59 | # create a dictionary containing all this | 124 | # create a dictionary containing all this |
| 60 | new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink} | 125 | new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, |
| 126 | 'sink': _sink} | ||
| 61 | # append the dictionary to the current list of slices | 127 | # append the dictionary to the current list of slices |
| 62 | slices.append(new_slice) | 128 | slices.append(new_slice) |
| 63 | 129 | ||
| @@ -69,18 +135,19 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 5 unmodified lines | |||
| 69 | start = max(start_stamp - total_frames, 0) | 135 | start = max(start_stamp - total_frames, 0) |
| 70 | # number of samples yet to written be until end of region | 136 | # number of samples yet to written be until end of region |
| 71 | remaining = end_stamp - total_frames + 1 | 137 | remaining = end_stamp - total_frames + 1 |
| 72 | #print current_slice, remaining, start | ||
| 73 | # not enough frames remaining, time to split | 138 | # not enough frames remaining, time to split |
| 74 | if remaining < read: | 139 | if remaining < read: |
| 75 | if remaining > start: | 140 | if remaining > start: |
| 76 | # write remaining samples from current region | 141 | # write remaining samples from current region |
| 77 | _sink.do_multi(vec[:, start:remaining], remaining - start) | 142 | _sink.do_multi(vec[:, start:remaining], remaining - start) |
| 78 | #print "closing region", "remaining", remaining | ||
| 79 | # close this file | 143 | # close this file |
| 80 | _sink.close() | 144 | _sink.close() |
| 81 | elif read > start: | 145 | elif read > start: |
| 82 | # write all the samples | 146 | # write all the samples |
| 83 | _sink.do_multi(vec[:, start:read], read - start) | 147 | _sink.do_multi(vec[:, start:read], read - start) |
| 84 | total_frames += read | 148 | total_frames += read |
| 149 | # remove old slices | ||
| 150 | slices = list(filter(lambda s: s['end_stamp'] > total_frames, | ||
| 151 | slices)) | ||
| 85 | if read < hopsize: | 152 | if read < hopsize: |
| 86 | break | 153 | break |
| @@ -2,6 +2,7 @@ aubiodefvalue = { · 1 unmodified line | |||
| 2 | # we have some clean up to do | 2 | # we have some clean up to do |
| 3 | 'buf_size': 'Py_default_vector_length', | 3 | 'buf_size': 'Py_default_vector_length', |
| 4 | 'win_s': 'Py_default_vector_length', | 4 | 'win_s': 'Py_default_vector_length', |
| 5 | 'size': 'Py_default_vector_length', | ||
| 5 | # and here too | 6 | # and here too |
| 6 | 'hop_size': 'Py_default_vector_length / 2', | 7 | 'hop_size': 'Py_default_vector_length / 2', |
| 7 | 'hop_s': 'Py_default_vector_length / 2', | 8 | 'hop_s': 'Py_default_vector_length / 2', |
| @@ -84,6 +85,7 @@ objoutsize = { · 76 unmodified lines | |||
| 84 | 'filterbank': 'self->n_filters', | 85 | 'filterbank': 'self->n_filters', |
| 85 | 'tss': 'self->buf_size', | 86 | 'tss': 'self->buf_size', |
| 86 | 'pitchshift': 'self->hop_size', | 87 | 'pitchshift': 'self->hop_size', |
| 88 | 'dct': 'self->size', | ||
| 87 | } | 89 | } |
| 88 | 90 | ||
| 89 | objinputsize = { | 91 | objinputsize = { |
| @@ -179,6 +181,10 @@ class MappedObject(object): · 89 unmodified lines | |||
| 179 | self.do_inputs = [get_params_types_names(self.do_proto)[1]] | 181 | self.do_inputs = [get_params_types_names(self.do_proto)[1]] |
| 180 | self.do_outputs = get_params_types_names(self.do_proto)[2:] | 182 | self.do_outputs = get_params_types_names(self.do_proto)[2:] |
| 181 | struct_output_str = ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in self.do_outputs] | 183 | struct_output_str = ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in self.do_outputs] |
| 184 | if len(self.prototypes['rdo']): | ||
| 185 | rdo_outputs = get_params_types_names(prototypes['rdo'][0])[2:] | ||
| 186 | struct_output_str += ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in rdo_outputs] | ||
| 187 | self.outputs += rdo_outputs | ||
| 182 | self.struct_outputs = ";\n ".join(struct_output_str) | 188 | self.struct_outputs = ";\n ".join(struct_output_str) |
| 183 | 189 | ||
| 184 | #print ("input_params: ", map(split_type, get_input_params(self.do_proto))) | 190 | #print ("input_params: ", map(split_type, get_input_params(self.do_proto))) |
| @@ -186,17 +192,26 @@ class MappedObject(object): · 1 unmodified line | |||
| 186 | 192 | ||
| 187 | def gen_code(self): | 193 | def gen_code(self): |
| 188 | out = "" | 194 | out = "" |
| 189 | out += self.gen_struct() | 195 | try: |
| 190 | out += self.gen_doc() | 196 | out += self.gen_struct() |
| 191 | out += self.gen_new() | 197 | out += self.gen_doc() |
| 192 | out += self.gen_init() | 198 | out += self.gen_new() |
| 193 | out += self.gen_del() | 199 | out += self.gen_init() |
| 194 | out += self.gen_do() | 200 | out += self.gen_del() |
| 195 | out += self.gen_memberdef() | 201 | out += self.gen_do() |
| 196 | out += self.gen_set() | 202 | if len(self.prototypes['rdo']): |
| 197 | out += self.gen_get() | 203 | self.do_proto = self.prototypes['rdo'][0] |
| 198 | out += self.gen_methodef() | 204 | self.do_inputs = [get_params_types_names(self.do_proto)[1]] |
| 199 | out += self.gen_typeobject() | 205 | self.do_outputs = get_params_types_names(self.do_proto)[2:] |
| 206 | out += self.gen_do(method='rdo') | ||
| 207 | out += self.gen_memberdef() | ||
| 208 | out += self.gen_set() | ||
| 209 | out += self.gen_get() | ||
| 210 | out += self.gen_methodef() | ||
| 211 | out += self.gen_typeobject() | ||
| 212 | except Exception as e: | ||
| 213 | print ("Failed generating code for", self.shortname) | ||
| 214 | raise | ||
| 200 | return out | 215 | return out |
| 201 | 216 | ||
| 202 | def gen_struct(self): | 217 | def gen_struct(self): |
| @@ -380,12 +395,12 @@ Py_{shortname}_del (Py_{shortname} * self, PyObject * unused) · 177 unmodified lines | |||
| 380 | """.format(del_fn = del_fn) | 395 | """.format(del_fn = del_fn) |
| 381 | return out | 396 | return out |
| 382 | 397 | ||
| 383 | def gen_do(self): | 398 | def gen_do(self, method = 'do'): |
| 384 | out = """ | 399 | out = """ |
| 385 | // do {shortname} | 400 | // do {shortname} |
| 386 | static PyObject* | 401 | static PyObject* |
| 387 | Py_{shortname}_do (Py_{shortname} * self, PyObject * args) | 402 | Pyaubio_{shortname}_{method} (Py_{shortname} * self, PyObject * args) |
| 388 | {{""".format(**self.__dict__) | 403 | {{""".format(method = method, **self.__dict__) |
| 389 | input_params = self.do_inputs | 404 | input_params = self.do_inputs |
| 390 | output_params = self.do_outputs | 405 | output_params = self.do_outputs |
| 391 | #print input_params | 406 | #print input_params |
| @@ -461,30 +476,51 @@ Py_{shortname}_do (Py_{shortname} * self, PyObject * args) · 69 unmodified lines | |||
| 461 | // {shortname} setters | 476 | // {shortname} setters |
| 462 | """.format(**self.__dict__) | 477 | """.format(**self.__dict__) |
| 463 | for set_param in self.prototypes['set']: | 478 | for set_param in self.prototypes['set']: |
| 464 | params = get_params_types_names(set_param)[1] | 479 | params = get_params_types_names(set_param)[1:] |
| 465 | paramtype = params['type'] | 480 | param = self.shortname.split('_set_')[-1] |
| 481 | paramdecls = "".join([""" | ||
| 482 | {0} {1};""".format(p['type'], p['name']) for p in params]) | ||
| 466 | method_name = get_name(set_param) | 483 | method_name = get_name(set_param) |
| 467 | param = method_name.split('aubio_'+self.shortname+'_set_')[-1] | 484 | param = method_name.split('aubio_'+self.shortname+'_set_')[-1] |
| 468 | pyparamtype = pyargparse_chars[paramtype] | 485 | refs = ", ".join(["&%s" % p['name'] for p in params]) |
| 486 | paramlist = ", ".join(["%s" % p['name'] for p in params]) | ||
| 487 | if len(params): | ||
| 488 | paramlist = "," + paramlist | ||
| 489 | pyparamtypes = ''.join([pyargparse_chars[p['type']] for p in params]) | ||
| 469 | out += """ | 490 | out += """ |
| 470 | static PyObject * | 491 | static PyObject * |
| 471 | Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args) | 492 | Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args) |
| 472 | {{ | 493 | {{ |
| 473 | uint_t err = 0; | 494 | uint_t err = 0; |
| 474 | {paramtype} {param}; | 495 | {paramdecls} |
| 496 | """.format(param = param, paramdecls = paramdecls, **self.__dict__) | ||
| 497 | |||
| 498 | if len(refs) and len(pyparamtypes): | ||
| 499 | out += """ | ||
| 475 | 500 | ||
| 476 | if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{ | 501 | if (!PyArg_ParseTuple (args, "{pyparamtypes}", {refs})) {{ |
| 477 | return NULL; | 502 | return NULL; |
| 478 | }} | 503 | }} |
| 479 | err = aubio_{shortname}_set_{param} (self->o, {param}); | 504 | """.format(pyparamtypes = pyparamtypes, refs = refs) |
| 505 | |||
| 506 | out += """ | ||
| 507 | err = aubio_{shortname}_set_{param} (self->o {paramlist}); | ||
| 480 | 508 | ||
| 481 | if (err > 0) {{ | 509 | if (err > 0) {{ |
| 482 | PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}"); | 510 | if (PyErr_Occurred() == NULL) {{ |
| 511 | PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}"); | ||
| 512 | }} else {{ | ||
| 513 | // change the RuntimeError into ValueError | ||
| 514 | PyObject *type, *value, *traceback; | ||
| 515 | PyErr_Fetch(&type, &value, &traceback); | ||
| 516 | PyErr_Restore(PyExc_ValueError, value, traceback); | ||
| 517 | }} | ||
| 483 | return NULL; | 518 | return NULL; |
| 484 | }} | 519 | }} |
| 485 | Py_RETURN_NONE; | 520 | Py_RETURN_NONE; |
| 486 | }} | 521 | }} |
| 487 | """.format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__) | 522 | """.format(param = param, refs = refs, paramdecls = paramdecls, |
| 523 | pyparamtypes = pyparamtypes, paramlist = paramlist, **self.__dict__) | ||
| 488 | return out | 524 | return out |
| 489 | 525 | ||
| 490 | def gen_get(self): | 526 | def gen_get(self): |
| @@ -525,6 +561,12 @@ static PyMethodDef Py_{shortname}_methods[] = {{""".format(**self.__dict__) · 34 unmodified lines | |||
| 525 | out += """ | 561 | out += """ |
| 526 | {{"{shortname}", (PyCFunction) Py{name}, | 562 | {{"{shortname}", (PyCFunction) Py{name}, |
| 527 | METH_NOARGS, ""}},""".format(name = name, shortname = shortname) | 563 | METH_NOARGS, ""}},""".format(name = name, shortname = shortname) |
| 564 | for m in self.prototypes['rdo']: | ||
| 565 | name = get_name(m) | ||
| 566 | shortname = name.replace('aubio_%s_' % self.shortname, '') | ||
| 567 | out += """ | ||
| 568 | {{"{shortname}", (PyCFunction) Py{name}, | ||
| 569 | METH_VARARGS, ""}},""".format(name = name, shortname = shortname) | ||
| 528 | out += """ | 570 | out += """ |
| 529 | {NULL} /* sentinel */ | 571 | {NULL} /* sentinel */ |
| 530 | }; | 572 | }; |
| @@ -550,7 +592,7 @@ PyTypeObject Py_{shortname}Type = {{ · 19 unmodified lines | |||
| 550 | 0, | 592 | 0, |
| 551 | 0, | 593 | 0, |
| 552 | 0, | 594 | 0, |
| 553 | (ternaryfunc)Py_{shortname}_do, | 595 | (ternaryfunc)Pyaubio_{shortname}_do, |
| 554 | 0, | 596 | 0, |
| 555 | 0, | 597 | 0, |
| 556 | 0, | 598 | 0, |
| @@ -1,5 +1,10 @@ | |||
| 1 | import distutils.ccompiler | 1 | import distutils.ccompiler |
| 2 | import sys, os, subprocess, glob | 2 | import sys |
| 3 | import os | ||
| 4 | import subprocess | ||
| 5 | import glob | ||
| 6 | from distutils.sysconfig import customize_compiler | ||
| 7 | from gen_code import MappedObject | ||
| 3 | 8 | ||
| 4 | header = os.path.join('src', 'aubio.h') | 9 | header = os.path.join('src', 'aubio.h') |
| 5 | output_path = os.path.join('python', 'gen') | 10 | output_path = os.path.join('python', 'gen') |
| @@ -8,43 +13,44 @@ source_header = """// this file is generated! do not modify · 2 unmodified lines | |||
| 8 | #include "aubio-types.h" | 13 | #include "aubio-types.h" |
| 9 | """ | 14 | """ |
| 10 | 15 | ||
| 11 | skip_objects = [ | 16 | default_skip_objects = [ |
| 12 | # already in ext/ | 17 | # already in ext/ |
| 13 | 'fft', | 18 | 'fft', |
| 14 | 'pvoc', | 19 | 'pvoc', |
| 15 | 'filter', | 20 | 'filter', |
| 16 | 'filterbank', | 21 | 'filterbank', |
| 17 | #'resampler', | 22 | # AUBIO_UNSTABLE |
| 18 | # AUBIO_UNSTABLE | 23 | 'hist', |
| 19 | 'hist', | 24 | 'parameter', |
| 20 | 'parameter', | 25 | 'scale', |
| 21 | 'scale', | 26 | 'beattracking', |
| 22 | 'beattracking', | 27 | 'resampler', |
| 23 | 'resampler', | 28 | 'peakpicker', |
| 24 | 'peakpicker', | 29 | 'pitchfcomb', |
| 25 | 'pitchfcomb', | 30 | 'pitchmcomb', |
| 26 | 'pitchmcomb', | 31 | 'pitchschmitt', |
| 27 | 'pitchschmitt', | 32 | 'pitchspecacf', |
| 28 | 'pitchspecacf', | 33 | 'pitchyin', |
| 29 | 'pitchyin', | 34 | 'pitchyinfft', |
| 30 | 'pitchyinfft', | 35 | 'pitchyinfast', |
| 31 | 'sink', | 36 | 'sink', |
| 32 | 'sink_apple_audio', | 37 | 'sink_apple_audio', |
| 33 | 'sink_sndfile', | 38 | 'sink_sndfile', |
| 34 | 'sink_wavwrite', | 39 | 'sink_wavwrite', |
| 35 | #'mfcc', | 40 | #'mfcc', |
| 36 | 'source', | 41 | 'source', |
| 37 | 'source_apple_audio', | 42 | 'source_apple_audio', |
| 38 | 'source_sndfile', | 43 | 'source_sndfile', |
| 39 | 'source_avcodec', | 44 | 'source_avcodec', |
| 40 | 'source_wavread', | 45 | 'source_wavread', |
| 41 | #'sampler', | 46 | #'sampler', |
| 42 | 'audio_unit', | 47 | 'audio_unit', |
| 43 | ] | 48 | 'spectral_whitening', |
| 49 | ] | ||
| 50 | |||
| 44 | 51 | ||
| 45 | def get_preprocessor(): | 52 | def get_preprocessor(): |
| 46 | # findout which compiler to use | 53 | # findout which compiler to use |
| 47 | from distutils.sysconfig import customize_compiler | ||
| 48 | compiler_name = distutils.ccompiler.get_default_compiler() | 54 | compiler_name = distutils.ccompiler.get_default_compiler() |
| 49 | compiler = distutils.ccompiler.new_compiler(compiler=compiler_name) | 55 | compiler = distutils.ccompiler.new_compiler(compiler=compiler_name) |
| 50 | try: | 56 | try: |
| @@ -59,26 +65,50 @@ def get_preprocessor(): · 8 unmodified lines | |||
| 59 | print("Warning: failed initializing compiler ({:s})".format(repr(e))) | 65 | print("Warning: failed initializing compiler ({:s})".format(repr(e))) |
| 60 | 66 | ||
| 61 | cpp_cmd = None | 67 | cpp_cmd = None |
| 62 | if hasattr(compiler, 'preprocessor'): # for unixccompiler | 68 | if hasattr(compiler, 'preprocessor'): # for unixccompiler |
| 63 | cpp_cmd = compiler.preprocessor | 69 | cpp_cmd = compiler.preprocessor |
| 64 | elif hasattr(compiler, 'compiler'): # for ccompiler | 70 | elif hasattr(compiler, 'compiler'): # for ccompiler |
| 65 | cpp_cmd = compiler.compiler.split() | 71 | cpp_cmd = compiler.compiler.split() |
| 66 | cpp_cmd += ['-E'] | 72 | cpp_cmd += ['-E'] |
| 67 | elif hasattr(compiler, 'cc'): # for msvccompiler | 73 | elif hasattr(compiler, 'cc'): # for msvccompiler |
| 68 | cpp_cmd = compiler.cc.split() | 74 | cpp_cmd = compiler.cc.split() |
| 69 | cpp_cmd += ['-E'] | 75 | cpp_cmd += ['-E'] |
| 70 | 76 | ||
| 77 | # On win-amd64 (py3.x), the default compiler is cross-compiling, from x86 | ||
| 78 | # to amd64 with %WIN_SDK_ROOT%\x86_amd64\cl.exe, but using this binary as a | ||
| 79 | # pre-processor generates no output, so we use %WIN_SDK_ROOT%\cl.exe | ||
| 80 | # instead. | ||
| 81 | if len(cpp_cmd) > 1 and 'cl.exe' in cpp_cmd[-2]: | ||
| 82 | plat = os.path.basename(os.path.dirname(cpp_cmd[-2])) | ||
| 83 | if plat == 'x86_amd64': | ||
| 84 | print('workaround on win64 to avoid empty pre-processor output') | ||
| 85 | cpp_cmd[-2] = cpp_cmd[-2].replace('x86_amd64', '') | ||
| 86 | elif True in ['amd64' in f for f in cpp_cmd]: | ||
| 87 | print('warning: not using workaround for', cpp_cmd[0], plat) | ||
| 88 | |||
| 71 | if not cpp_cmd: | 89 | if not cpp_cmd: |
| 72 | print("Warning: could not guess preprocessor, using env's CC") | 90 | print("Warning: could not guess preprocessor, using env's CC") |
| 73 | cpp_cmd = os.environ.get('CC', 'cc').split() | 91 | cpp_cmd = os.environ.get('CC', 'cc').split() |
| 74 | cpp_cmd += ['-E'] | 92 | cpp_cmd += ['-E'] |
| 75 | 93 | if 'emcc' in cpp_cmd: | |
| 94 | cpp_cmd += ['-x', 'c'] # emcc defaults to c++, force C language | ||
| 76 | return cpp_cmd | 95 | return cpp_cmd |
| 77 | 96 | ||
| 78 | def get_cpp_objects(header=header): | 97 | |
| 98 | def get_c_declarations(header=header, usedouble=False): | ||
| 99 | ''' return a dense and preprocessed string of all c declarations implied by aubio.h | ||
| 100 | ''' | ||
| 101 | cpp_output = get_cpp_output(header=header, usedouble=usedouble) | ||
| 102 | return filter_cpp_output (cpp_output) | ||
| 103 | |||
| 104 | |||
| 105 | def get_cpp_output(header=header, usedouble=False): | ||
| 106 | ''' find and run a C pre-processor on aubio.h ''' | ||
| 79 | cpp_cmd = get_preprocessor() | 107 | cpp_cmd = get_preprocessor() |
| 80 | 108 | ||
| 81 | macros = [('AUBIO_UNSTABLE', 1)] | 109 | macros = [('AUBIO_UNSTABLE', 1)] |
| 110 | if usedouble: | ||
| 111 | macros += [('HAVE_AUBIO_DOUBLE', 1)] | ||
| 82 | 112 | ||
| 83 | if not os.path.isfile(header): | 113 | if not os.path.isfile(header): |
| 84 | raise Exception("could not find include file " + header) | 114 | raise Exception("could not find include file " + header) |
| @@ -89,56 +119,115 @@ def get_cpp_objects(header=header): · 4 unmodified lines | |||
| 89 | 119 | ||
| 90 | print("Running command: {:s}".format(" ".join(cpp_cmd))) | 120 | print("Running command: {:s}".format(" ".join(cpp_cmd))) |
| 91 | proc = subprocess.Popen(cpp_cmd, | 121 | proc = subprocess.Popen(cpp_cmd, |
| 92 | stderr=subprocess.PIPE, | 122 | stderr=subprocess.PIPE, |
| 93 | stdout=subprocess.PIPE) | 123 | stdout=subprocess.PIPE) |
| 94 | assert proc, 'Proc was none' | 124 | assert proc, 'Proc was none' |
| 95 | cpp_output = proc.stdout.read() | 125 | cpp_output = proc.stdout.read() |
| 96 | err_output = proc.stderr.read() | 126 | err_output = proc.stderr.read() |
| 127 | if err_output: | ||
| 128 | print("Warning: preprocessor produced errors or warnings:\n%s" \ | ||
| 129 | % err_output.decode('utf8')) | ||
| 97 | if not cpp_output: | 130 | if not cpp_output: |
| 98 | raise Exception("preprocessor output is empty:\n%s" % err_output) | 131 | raise_msg = "preprocessor output is empty! Running command " \ |
| 99 | elif err_output: | 132 | + "\"%s\" failed" % " ".join(cpp_cmd) |
| 100 | print ("Warning: preprocessor produced warnings:\n%s" % err_output) | 133 | if err_output: |
| 134 | raise_msg += " with stderr: \"%s\"" % err_output.decode('utf8') | ||
| 135 | else: | ||
| 136 | raise_msg += " with no stdout or stderr" | ||
| 137 | raise Exception(raise_msg) | ||
| 101 | if not isinstance(cpp_output, list): | 138 | if not isinstance(cpp_output, list): |
| 102 | cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')] | 139 | cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')] |
| 103 | 140 | ||
| 104 | cpp_output = filter(lambda y: len(y) > 1, cpp_output) | 141 | return cpp_output |
| 142 | |||
| 143 | def filter_cpp_output(cpp_raw_output): | ||
| 144 | ''' prepare cpp-output for parsing ''' | ||
| 145 | cpp_output = filter(lambda y: len(y) > 1, cpp_raw_output) | ||
| 105 | cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output)) | 146 | cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output)) |
| 106 | 147 | ||
| 107 | i = 1 | 148 | i = 1 |
| 108 | while 1: | 149 | while 1: |
| 109 | if i >= len(cpp_output): break | 150 | if i >= len(cpp_output): |
| 110 | if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'): | 151 | break |
| 111 | cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i] | 152 | if ('{' in cpp_output[i - 1]) and ('}' not in cpp_output[i - 1]) or (';' not in cpp_output[i - 1]): |
| 112 | cpp_output.pop(i-1) | 153 | cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i] |
| 154 | cpp_output.pop(i - 1) | ||
| 155 | elif ('}' in cpp_output[i]): | ||
| 156 | cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i] | ||
| 157 | cpp_output.pop(i - 1) | ||
| 113 | else: | 158 | else: |
| 114 | i += 1 | 159 | i += 1 |
| 115 | 160 | ||
| 116 | typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output) | 161 | # clean pointer notations |
| 162 | tmp = [] | ||
| 163 | for l in cpp_output: | ||
| 164 | tmp += [l.replace(' *', ' * ')] | ||
| 165 | cpp_output = tmp | ||
| 117 | 166 | ||
| 167 | return cpp_output | ||
| 168 | |||
| 169 | |||
| 170 | def get_cpp_objects_from_c_declarations(c_declarations, skip_objects=None): | ||
| 171 | if skip_objects is None: | ||
| 172 | skip_objects = default_skip_objects | ||
| 173 | typedefs = filter(lambda y: y.startswith('typedef struct _aubio'), c_declarations) | ||
| 118 | cpp_objects = [a.split()[3][:-1] for a in typedefs] | 174 | cpp_objects = [a.split()[3][:-1] for a in typedefs] |
| 175 | cpp_objects_filtered = filter(lambda y: not y[6:-2] in skip_objects, cpp_objects) | ||
| 176 | return cpp_objects_filtered | ||
| 177 | |||
| 178 | |||
| 179 | def get_all_func_names_from_lib(lib): | ||
| 180 | ''' return flat string of all function used in lib | ||
| 181 | ''' | ||
| 182 | res = [] | ||
| 183 | for _, v in lib.items(): | ||
| 184 | if isinstance(v, dict): | ||
| 185 | res += get_all_func_names_from_lib(v) | ||
| 186 | elif isinstance(v, list): | ||
| 187 | for elem in v: | ||
| 188 | e = elem.split('(') | ||
| 189 | if len(e) < 2: | ||
| 190 | continue # not a function | ||
| 191 | fname_part = e[0].strip().split(' ') | ||
| 192 | fname = fname_part[-1] | ||
| 193 | if fname: | ||
| 194 | res += [fname] | ||
| 195 | else: | ||
| 196 | raise NameError('gen_lib : weird function: ' + str(e)) | ||
| 119 | 197 | ||
| 120 | return cpp_output, cpp_objects | 198 | return res |
| 121 | 199 | ||
| 122 | 200 | ||
| 123 | def analyze_cpp_output(cpp_objects, cpp_output): | 201 | def generate_lib_from_c_declarations(cpp_objects, c_declarations): |
| 202 | ''' returns a lib from given cpp_object names | ||
| 203 | |||
| 204 | a lib is a dict grouping functions by family (onset,pitch...) | ||
| 205 | each eement is itself a dict of functions grouped by puposes as : | ||
| 206 | struct, new, del, do, get, set and other | ||
| 207 | ''' | ||
| 124 | lib = {} | 208 | lib = {} |
| 125 | 209 | ||
| 126 | for o in cpp_objects: | 210 | for o in cpp_objects: |
| 127 | if o[:6] != 'aubio_': | 211 | shortname = o |
| 128 | continue | 212 | if o[:6] == 'aubio_': |
| 129 | shortname = o[6:-2] | 213 | shortname = o[6:-2] # without aubio_ prefix and _t suffix |
| 130 | if shortname in skip_objects: | 214 | |
| 131 | continue | 215 | lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'rdo': [], 'get': [], 'set': [], 'other': []} |
| 132 | lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []} | ||
| 133 | lib[shortname]['longname'] = o | 216 | lib[shortname]['longname'] = o |
| 134 | lib[shortname]['shortname'] = shortname | 217 | lib[shortname]['shortname'] = shortname |
| 135 | for fn in cpp_output: | 218 | |
| 136 | if o[:-1] in fn: | 219 | fullshortname = o[:-2] # name without _t suffix |
| 137 | #print "found", o[:-1], "in", fn | 220 | |
| 221 | for fn in c_declarations: | ||
| 222 | func_name = fn.split('(')[0].strip().split(' ')[-1] | ||
| 223 | if func_name.startswith(fullshortname + '_') or func_name.endswith(fullshortname): | ||
| 224 | # print "found", shortname, "in", fn | ||
| 138 | if 'typedef struct ' in fn: | 225 | if 'typedef struct ' in fn: |
| 139 | lib[shortname]['struct'].append(fn) | 226 | lib[shortname]['struct'].append(fn) |
| 140 | elif '_do' in fn: | 227 | elif '_do' in fn: |
| 141 | lib[shortname]['do'].append(fn) | 228 | lib[shortname]['do'].append(fn) |
| 229 | elif '_rdo' in fn: | ||
| 230 | lib[shortname]['rdo'].append(fn) | ||
| 142 | elif 'new_' in fn: | 231 | elif 'new_' in fn: |
| 143 | lib[shortname]['new'].append(fn) | 232 | lib[shortname]['new'].append(fn) |
| 144 | elif 'del_' in fn: | 233 | elif 'del_' in fn: |
| @@ -148,52 +237,52 @@ def analyze_cpp_output(cpp_objects, cpp_output): · 3 unmodified lines | |||
| 148 | elif '_set_' in fn: | 237 | elif '_set_' in fn: |
| 149 | lib[shortname]['set'].append(fn) | 238 | lib[shortname]['set'].append(fn) |
| 150 | else: | 239 | else: |
| 151 | #print "no idea what to do about", fn | 240 | # print "no idea what to do about", fn |
| 152 | lib[shortname]['other'].append(fn) | 241 | lib[shortname]['other'].append(fn) |
| 153 | return lib | 242 | return lib |
| 154 | 243 | ||
| 155 | def print_cpp_output_results(lib, cpp_output): | 244 | |
| 156 | for fn in cpp_output: | 245 | def print_c_declarations_results(lib, c_declarations): |
| 246 | for fn in c_declarations: | ||
| 157 | found = 0 | 247 | found = 0 |
| 158 | for o in lib: | 248 | for o in lib: |
| 159 | for family in lib[o]: | 249 | for family in lib[o]: |
| 160 | if fn in lib[o][family]: | 250 | if fn in lib[o][family]: |
| 161 | found = 1 | 251 | found = 1 |
| 162 | if found == 0: | 252 | if found == 0: |
| 163 | print ("missing", fn) | 253 | print("missing", fn) |
| 164 | 254 | ||
| 165 | for o in lib: | 255 | for o in lib: |
| 166 | for family in lib[o]: | 256 | for family in lib[o]: |
| 167 | if type(lib[o][family]) == str: | 257 | if type(lib[o][family]) == str: |
| 168 | print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) ) | 258 | print("{:15s} {:10s} {:s}".format(o, family, lib[o][family])) |
| 169 | elif len(lib[o][family]) == 1: | 259 | elif len(lib[o][family]) == 1: |
| 170 | print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ) ) | 260 | print("{:15s} {:10s} {:s}".format(o, family, lib[o][family][0])) |
| 171 | else: | 261 | else: |
| 172 | print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) ) | 262 | print("{:15s} {:10s} {:s}".format(o, family, lib[o][family])) |
| 173 | 263 | ||
| 174 | 264 | ||
| 175 | def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True): | 265 | def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True): |
| 176 | if not os.path.isdir(output_path): os.mkdir(output_path) | 266 | if not os.path.isdir(output_path): |
| 177 | elif not overwrite: return glob.glob(os.path.join(output_path, '*.c')) | 267 | os.mkdir(output_path) |
| 268 | elif not overwrite: | ||
| 269 | return sorted(glob.glob(os.path.join(output_path, '*.c'))) | ||
| 178 | 270 | ||
| 179 | cpp_output, cpp_objects = get_cpp_objects(header) | 271 | c_declarations = get_c_declarations(header, usedouble=usedouble) |
| 272 | cpp_objects = get_cpp_objects_from_c_declarations(c_declarations) | ||
| 180 | 273 | ||
| 181 | lib = analyze_cpp_output(cpp_objects, cpp_output) | 274 | lib = generate_lib_from_c_declarations(cpp_objects, c_declarations) |
| 182 | # print_cpp_output_results(lib, cpp_output) | 275 | # print_c_declarations_results(lib, c_declarations) |
| 183 | 276 | ||
| 184 | sources_list = [] | 277 | sources_list = [] |
| 185 | try: | ||
| 186 | from .gen_code import MappedObject | ||
| 187 | except (SystemError, ValueError): | ||
| 188 | from gen_code import MappedObject | ||
| 189 | for o in lib: | 278 | for o in lib: |
| 190 | out = source_header | 279 | out = source_header |
| 191 | mapped = MappedObject(lib[o], usedouble = usedouble) | 280 | mapped = MappedObject(lib[o], usedouble=usedouble) |
| 192 | out += mapped.gen_code() | 281 | out += mapped.gen_code() |
| 193 | output_file = os.path.join(output_path, 'gen-%s.c' % o) | 282 | output_file = os.path.join(output_path, 'gen-%s.c' % o) |
| 194 | with open(output_file, 'w') as f: | 283 | with open(output_file, 'w') as f: |
| 195 | f.write(out) | 284 | f.write(out) |
| 196 | print ("wrote %s" % output_file ) | 285 | print("wrote %s" % output_file) |
| 197 | sources_list.append(output_file) | 286 | sources_list.append(output_file) |
| 198 | 287 | ||
| 199 | out = source_header | 288 | out = source_header |
| @@ -205,23 +294,23 @@ int generated_types_ready (void) · 5 unmodified lines | |||
| 205 | {{ | 294 | {{ |
| 206 | return ({pycheck_types}); | 295 | return ({pycheck_types}); |
| 207 | }} | 296 | }} |
| 208 | """.format(pycheck_types = check_types) | 297 | """.format(pycheck_types=check_types) |
| 209 | 298 | ||
| 210 | add_types = "".join([""" | 299 | add_types = "".join([""" |
| 211 | Py_INCREF (&Py_{name}Type); | 300 | Py_INCREF (&Py_{name}Type); |
| 212 | PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name = o) for o in lib]) | 301 | PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name=o) for o in lib]) |
| 213 | out += """ | 302 | out += """ |
| 214 | 303 | ||
| 215 | void add_generated_objects ( PyObject *m ) | 304 | void add_generated_objects ( PyObject *m ) |
| 216 | {{ | 305 | {{ |
| 217 | {add_types} | 306 | {add_types} |
| 218 | }} | 307 | }} |
| 219 | """.format(add_types = add_types) | 308 | """.format(add_types=add_types) |
| 220 | 309 | ||
| 221 | output_file = os.path.join(output_path, 'aubio-generated.c') | 310 | output_file = os.path.join(output_path, 'aubio-generated.c') |
| 222 | with open(output_file, 'w') as f: | 311 | with open(output_file, 'w') as f: |
| 223 | f.write(out) | 312 | f.write(out) |
| 224 | print ("wrote %s" % output_file ) | 313 | print("wrote %s" % output_file) |
| 225 | sources_list.append(output_file) | 314 | sources_list.append(output_file) |
| 226 | 315 | ||
| 227 | objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib]) | 316 | objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib]) |
| @@ -239,17 +328,19 @@ void add_generated_objects ( PyObject *m ) · 11 unmodified lines | |||
| 239 | {objlist} | 328 | {objlist} |
| 240 | int generated_objects ( void ); | 329 | int generated_objects ( void ); |
| 241 | void add_generated_objects( PyObject *m ); | 330 | void add_generated_objects( PyObject *m ); |
| 242 | """.format(objlist = objlist) | 331 | """.format(objlist=objlist) |
| 243 | 332 | ||
| 244 | output_file = os.path.join(output_path, 'aubio-generated.h') | 333 | output_file = os.path.join(output_path, 'aubio-generated.h') |
| 245 | with open(output_file, 'w') as f: | 334 | with open(output_file, 'w') as f: |
| 246 | f.write(out) | 335 | f.write(out) |
| 247 | print ("wrote %s" % output_file ) | 336 | print("wrote %s" % output_file) |
| 248 | # no need to add header to list of sources | 337 | # no need to add header to list of sources |
| 249 | 338 | ||
| 250 | return sources_list | 339 | return sorted(sources_list) |
| 251 | 340 | ||
| 252 | if __name__ == '__main__': | 341 | if __name__ == '__main__': |
| 253 | if len(sys.argv) > 1: header = sys.argv[1] | 342 | if len(sys.argv) > 1: |
| 254 | if len(sys.argv) > 2: output_path = sys.argv[2] | 343 | header = sys.argv[1] |
| 344 | if len(sys.argv) > 2: | ||
| 345 | output_path = sys.argv[2] | ||
| 255 | generate_external(header, output_path) | 346 | generate_external(header, output_path) |
| @@ -2,7 +2,9 @@ · 1 unmodified line | |||
| 2 | # | 2 | # |
| 3 | import sys, os, glob, subprocess | 3 | import sys, os, glob, subprocess |
| 4 | import distutils, distutils.command.clean, distutils.dir_util | 4 | import distutils, distutils.command.clean, distutils.dir_util |
| 5 | from .gen_external import generate_external, header, output_path | 5 | from gen_external import generate_external, header, output_path |
| 6 | |||
| 7 | from this_version import get_aubio_version | ||
| 6 | 8 | ||
| 7 | # inspired from https://gist.github.com/abergmeier/9488990 | 9 | # inspired from https://gist.github.com/abergmeier/9488990 |
| 8 | def add_packages(packages, ext=None, **kw): | 10 | def add_packages(packages, ext=None, **kw): |
| @@ -21,6 +23,7 @@ def add_packages(packages, ext=None, **kw): · 12 unmodified lines | |||
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | for package in packages: | 25 | for package in packages: |
| 26 | print("checking for {:s}".format(package)) | ||
| 24 | cmd = ['pkg-config', '--libs', '--cflags', package] | 27 | cmd = ['pkg-config', '--libs', '--cflags', package] |
| 25 | try: | 28 | try: |
| 26 | tokens = subprocess.check_output(cmd) | 29 | tokens = subprocess.check_output(cmd) |
| @@ -54,13 +57,14 @@ def add_local_aubio_lib(ext): · 27 unmodified lines | |||
| 54 | 57 | ||
| 55 | def add_local_aubio_sources(ext): | 58 | def add_local_aubio_sources(ext): |
| 56 | """ build aubio inside python module instead of linking against libaubio """ | 59 | """ build aubio inside python module instead of linking against libaubio """ |
| 57 | print("Warning: libaubio was not built with waf, adding src/") | 60 | print("Info: libaubio was not installed or built locally with waf, adding src/") |
| 58 | # create an empty header, macros will be passed on the command line | 61 | aubio_sources = sorted(glob.glob(os.path.join('src', '**.c'))) |
| 59 | fake_config_header = os.path.join('python', 'ext', 'config.h') | 62 | aubio_sources += sorted(glob.glob(os.path.join('src', '*', '**.c'))) |
| 60 | distutils.file_util.write_file(fake_config_header, "") | ||
| 61 | aubio_sources = glob.glob(os.path.join('src', '**.c')) | ||
| 62 | aubio_sources += glob.glob(os.path.join('src', '*', '**.c')) | ||
| 63 | ext.sources += aubio_sources | 63 | ext.sources += aubio_sources |
| 64 | |||
| 65 | def add_local_macros(ext, usedouble = False): | ||
| 66 | if usedouble: | ||
| 67 | ext.define_macros += [('HAVE_AUBIO_DOUBLE', 1)] | ||
| 64 | # define macros (waf puts them in build/src/config.h) | 68 | # define macros (waf puts them in build/src/config.h) |
| 65 | for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H', | 69 | for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H', |
| 66 | 'HAVE_MATH_H', 'HAVE_STRING_H', | 70 | 'HAVE_MATH_H', 'HAVE_STRING_H', |
| @@ -69,22 +73,31 @@ def add_local_aubio_sources(ext): · 2 unmodified lines | |||
| 69 | 'HAVE_MEMCPY_HACKS']: | 73 | 'HAVE_MEMCPY_HACKS']: |
| 70 | ext.define_macros += [(define_macro, 1)] | 74 | ext.define_macros += [(define_macro, 1)] |
| 71 | 75 | ||
| 76 | def add_external_deps(ext, usedouble = False): | ||
| 72 | # loof for additional packages | 77 | # loof for additional packages |
| 73 | print("Info: looking for *optional* additional packages") | 78 | print("Info: looking for *optional* additional packages") |
| 74 | packages = ['libavcodec', 'libavformat', 'libavutil', 'libavresample', | 79 | packages = ['libavcodec', 'libavformat', 'libavutil', |
| 80 | 'libswresample', 'libavresample', | ||
| 75 | 'jack', | 81 | 'jack', |
| 76 | 'sndfile', 'samplerate', | 82 | 'sndfile', |
| 77 | 'rubberband', | 83 | 'rubberband', |
| 78 | #'fftw3f', | 84 | #'fftw3f', |
| 79 | ] | 85 | ] |
| 86 | # samplerate only works with float | ||
| 87 | if usedouble is False: | ||
| 88 | packages += ['samplerate'] | ||
| 89 | else: | ||
| 90 | print("Info: not adding libsamplerate in double precision mode") | ||
| 80 | add_packages(packages, ext=ext) | 91 | add_packages(packages, ext=ext) |
| 81 | if 'avcodec' in ext.libraries \ | 92 | if 'avcodec' in ext.libraries \ |
| 82 | and 'avformat' in ext.libraries \ | 93 | and 'avformat' in ext.libraries \ |
| 83 | and 'avutil' in ext.libraries \ | 94 | and 'avutil' in ext.libraries: |
| 84 | and 'avresample' in ext.libraries: | 95 | if 'swresample' in ext.libraries: |
| 85 | ext.define_macros += [('HAVE_LIBAV', 1)] | 96 | ext.define_macros += [('HAVE_SWRESAMPLE', 1)] |
| 86 | if 'jack' in ext.libraries: | 97 | elif 'avresample' in ext.libraries: |
| 87 | ext.define_macros += [('HAVE_JACK', 1)] | 98 | ext.define_macros += [('HAVE_AVRESAMPLE', 1)] |
| 99 | if 'swresample' in ext.libraries or 'avresample' in ext.libraries: | ||
| 100 | ext.define_macros += [('HAVE_LIBAV', 1)] | ||
| 88 | if 'sndfile' in ext.libraries: | 101 | if 'sndfile' in ext.libraries: |
| 89 | ext.define_macros += [('HAVE_SNDFILE', 1)] | 102 | ext.define_macros += [('HAVE_SNDFILE', 1)] |
| 90 | if 'samplerate' in ext.libraries: | 103 | if 'samplerate' in ext.libraries: |
| @@ -107,39 +120,78 @@ def add_local_aubio_sources(ext): · 16 unmodified lines | |||
| 107 | 120 | ||
| 108 | ext.define_macros += [('HAVE_WAVWRITE', 1)] | 121 | ext.define_macros += [('HAVE_WAVWRITE', 1)] |
| 109 | ext.define_macros += [('HAVE_WAVREAD', 1)] | 122 | ext.define_macros += [('HAVE_WAVREAD', 1)] |
| 110 | # TODO: | 123 | |
| 111 | # add cblas | 124 | # TODO: add cblas |
| 112 | if 0: | 125 | if 0: |
| 113 | ext.libraries += ['cblas'] | 126 | ext.libraries += ['cblas'] |
| 114 | ext.define_macros += [('HAVE_ATLAS_CBLAS_H', 1)] | 127 | ext.define_macros += [('HAVE_ATLAS_CBLAS_H', 1)] |
| 115 | 128 | ||
| 116 | def add_system_aubio(ext): | 129 | def add_system_aubio(ext): |
| 117 | # use pkg-config to find aubio's location | 130 | # use pkg-config to find aubio's location |
| 118 | add_packages(['aubio'], ext) | 131 | aubio_version = get_aubio_version() |
| 132 | add_packages(['aubio = ' + aubio_version], ext) | ||
| 119 | if 'aubio' not in ext.libraries: | 133 | if 'aubio' not in ext.libraries: |
| 120 | print("Error: libaubio not found") | 134 | print("Info: aubio " + aubio_version + " was not found by pkg-config") |
| 135 | else: | ||
| 136 | print("Info: using system aubio " + aubio_version + " found in " + ' '.join(ext.library_dirs)) | ||
| 137 | |||
| 138 | def add_libav_on_win(ext): | ||
| 139 | """ no pkg-config on windows, simply assume these libs are available """ | ||
| 140 | ext.libraries += ['avformat', 'avutil', 'avcodec', 'swresample'] | ||
| 141 | for define_macro in ['HAVE_LIBAV', 'HAVE_SWRESAMPLE']: | ||
| 142 | ext.define_macros += [(define_macro, 1)] | ||
| 121 | 143 | ||
| 122 | class CleanGenerated(distutils.command.clean.clean): | 144 | class CleanGenerated(distutils.command.clean.clean): |
| 123 | def run(self): | 145 | def run(self): |
| 124 | distutils.dir_util.remove_tree(output_path) | 146 | if os.path.isdir(output_path): |
| 125 | distutils.command.clean.clean.run(self) | 147 | distutils.dir_util.remove_tree(output_path) |
| 148 | |||
| 149 | from distutils.command.build_ext import build_ext as _build_ext | ||
| 150 | class build_ext(_build_ext): | ||
| 126 | 151 | ||
| 127 | class GenerateCommand(distutils.cmd.Command): | 152 | user_options = _build_ext.user_options + [ |
| 128 | description = 'generate gen/gen-*.c files from ../src/aubio.h' | ||
| 129 | user_options = [ | ||
| 130 | # The format is (long option, short option, description). | 153 | # The format is (long option, short option, description). |
| 131 | ('enable-double', None, 'use HAVE_AUBIO_DOUBLE=1 (default: 0)'), | 154 | ('enable-double', None, 'use HAVE_AUBIO_DOUBLE=1 (default: 0)'), |
| 132 | ] | 155 | ] |
| 133 | 156 | ||
| 134 | def initialize_options(self): | 157 | def initialize_options(self): |
| 158 | _build_ext.initialize_options(self) | ||
| 135 | self.enable_double = False | 159 | self.enable_double = False |
| 136 | 160 | ||
| 137 | def finalize_options(self): | 161 | def finalize_options(self): |
| 162 | _build_ext.finalize_options(self) | ||
| 138 | if self.enable_double: | 163 | if self.enable_double: |
| 139 | self.announce( | 164 | self.announce( |
| 140 | 'will generate code for aubio compiled with HAVE_AUBIO_DOUBLE=1', | 165 | 'will generate code for aubio compiled with HAVE_AUBIO_DOUBLE=1', |
| 141 | level=distutils.log.INFO) | 166 | level=distutils.log.INFO) |
| 142 | 167 | ||
| 143 | def run(self): | 168 | def build_extension(self, extension): |
| 144 | self.announce( 'Generating code', level=distutils.log.INFO) | 169 | if self.enable_double or 'HAVE_AUBIO_DOUBLE' in os.environ: |
| 145 | generated_object_files = generate_external(header, output_path, usedouble=self.enable_double) | 170 | enable_double = True |
| 171 | else: | ||
| 172 | enable_double = False | ||
| 173 | # seack for aubio headers and lib in PKG_CONFIG_PATH | ||
| 174 | add_system_aubio(extension) | ||
| 175 | # the lib was not installed on this system | ||
| 176 | if 'aubio' not in extension.libraries: | ||
| 177 | # use local src/aubio.h | ||
| 178 | if os.path.isfile(os.path.join('src', 'aubio.h')): | ||
| 179 | add_local_aubio_header(extension) | ||
| 180 | add_local_macros(extension, usedouble=enable_double) | ||
| 181 | # look for a local waf build | ||
| 182 | if os.path.isfile(os.path.join('build','src', 'fvec.c.1.o')): | ||
| 183 | add_local_aubio_lib(extension) | ||
| 184 | else: | ||
| 185 | # check for external dependencies | ||
| 186 | add_external_deps(extension, usedouble=enable_double) | ||
| 187 | # force adding libav on windows | ||
| 188 | if os.name == 'nt' and ('WITH_LIBAV' in os.environ \ | ||
| 189 | or 'CONDA_PREFIX' in os.environ): | ||
| 190 | add_libav_on_win(extension) | ||
| 191 | # add libaubio sources and look for optional deps with pkg-config | ||
| 192 | add_local_aubio_sources(extension) | ||
| 193 | # generate files python/gen/*.c, python/gen/aubio-generated.h | ||
| 194 | extension.include_dirs += [ output_path ] | ||
| 195 | extension.sources += generate_external(header, output_path, overwrite = False, | ||
| 196 | usedouble=enable_double) | ||
| 197 | return _build_ext.build_extension(self, extension) | ||
| @@ -1,206 +0,0 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """ this file was written by Paul Brossier | ||
| 4 | it is released under the GNU/GPL license. | ||
| 5 | """ | ||
| 6 | |||
| 7 | import sys | ||
| 8 | #from aubio.task import * | ||
| 9 | |||
| 10 | usage = "usage: %s [options] -i soundfile" % sys.argv[0] | ||
| 11 | usage += "\n help: %s -h" % sys.argv[0] | ||
| 12 | |||
| 13 | def parse_args(): | ||
| 14 | from optparse import OptionParser | ||
| 15 | parser = OptionParser(usage=usage) | ||
| 16 | parser.add_option("-i", "--input", action = "store", dest = "source_file", | ||
| 17 | help="input sound file to analyse", metavar = "<source_file>") | ||
| 18 | parser.add_option("-O","--onset-method", | ||
| 19 | action="store", dest="onset_method", default='default', | ||
| 20 | metavar = "<onset_method>", | ||
| 21 | help="onset detection method [default=default] \ | ||
| 22 | complexdomain|hfc|phase|specdiff|energy|kl|mkl") | ||
| 23 | # cutting methods | ||
| 24 | parser.add_option("-b","--beat", | ||
| 25 | action="store_true", dest="beat", default=False, | ||
| 26 | help="use beat locations") | ||
| 27 | """ | ||
| 28 | parser.add_option("-S","--silencecut", | ||
| 29 | action="store_true", dest="silencecut", default=False, | ||
| 30 | help="use silence locations") | ||
| 31 | parser.add_option("-s","--silence", | ||
| 32 | metavar = "<value>", | ||
| 33 | action="store", dest="silence", default=-70, | ||
| 34 | help="silence threshold [default=-70]") | ||
| 35 | """ | ||
| 36 | # algorithm parameters | ||
| 37 | parser.add_option("-r", "--samplerate", | ||
| 38 | metavar = "<freq>", type='int', | ||
| 39 | action="store", dest="samplerate", default=0, | ||
| 40 | help="samplerate at which the file should be represented") | ||
| 41 | parser.add_option("-B","--bufsize", | ||
| 42 | action="store", dest="bufsize", default=512, | ||
| 43 | metavar = "<size>", type='int', | ||
| 44 | help="buffer size [default=512]") | ||
| 45 | parser.add_option("-H","--hopsize", | ||
| 46 | metavar = "<size>", type='int', | ||
| 47 | action="store", dest="hopsize", default=256, | ||
| 48 | help="overlap size [default=256]") | ||
| 49 | parser.add_option("-t","--onset-threshold", | ||
| 50 | metavar = "<value>", type="float", | ||
| 51 | action="store", dest="threshold", default=0.3, | ||
| 52 | help="onset peak picking threshold [default=0.3]") | ||
| 53 | parser.add_option("-c","--cut", | ||
| 54 | action="store_true", dest="cut", default=False, | ||
| 55 | help="cut input sound file at detected labels \ | ||
| 56 | best used with option -L") | ||
| 57 | |||
| 58 | # minioi | ||
| 59 | parser.add_option("-M","--minioi", | ||
| 60 | metavar = "<value>", type='string', | ||
| 61 | action="store", dest="minioi", default="12ms", | ||
| 62 | help="minimum inter onset interval [default=12ms]") | ||
| 63 | |||
| 64 | """ | ||
| 65 | parser.add_option("-D","--delay", | ||
| 66 | action = "store", dest = "delay", type = "float", | ||
| 67 | metavar = "<seconds>", default=0, | ||
| 68 | help="number of seconds to take back [default=system]\ | ||
| 69 | default system delay is 3*hopsize/samplerate") | ||
| 70 | parser.add_option("-C","--dcthreshold", | ||
| 71 | metavar = "<value>", | ||
| 72 | action="store", dest="dcthreshold", default=1., | ||
| 73 | help="onset peak picking DC component [default=1.]") | ||
| 74 | parser.add_option("-L","--localmin", | ||
| 75 | action="store_true", dest="localmin", default=False, | ||
| 76 | help="use local minima after peak detection") | ||
| 77 | parser.add_option("-d","--derivate", | ||
| 78 | action="store_true", dest="derivate", default=False, | ||
| 79 | help="derivate onset detection function") | ||
| 80 | parser.add_option("-z","--zerocross", | ||
| 81 | metavar = "<value>", | ||
| 82 | action="store", dest="zerothres", default=0.008, | ||
| 83 | help="zero-crossing threshold for slicing [default=0.00008]") | ||
| 84 | """ | ||
| 85 | # plotting functions | ||
| 86 | """ | ||
| 87 | parser.add_option("-p","--plot", | ||
| 88 | action="store_true", dest="plot", default=False, | ||
| 89 | help="draw plot") | ||
| 90 | parser.add_option("-x","--xsize", | ||
| 91 | metavar = "<size>", | ||
| 92 | action="store", dest="xsize", default=1., | ||
| 93 | type='float', help="define xsize for plot") | ||
| 94 | parser.add_option("-y","--ysize", | ||
| 95 | metavar = "<size>", | ||
| 96 | action="store", dest="ysize", default=1., | ||
| 97 | type='float', help="define ysize for plot") | ||
| 98 | parser.add_option("-f","--function", | ||
| 99 | action="store_true", dest="func", default=False, | ||
| 100 | help="print detection function") | ||
| 101 | parser.add_option("-n","--no-onsets", | ||
| 102 | action="store_true", dest="nplot", default=False, | ||
| 103 | help="do not plot detected onsets") | ||
| 104 | parser.add_option("-O","--outplot", | ||
| 105 | metavar = "<output_image>", | ||
| 106 | action="store", dest="outplot", default=None, | ||
| 107 | help="save plot to output.{ps,png}") | ||
| 108 | parser.add_option("-F","--spectrogram", | ||
| 109 | action="store_true", dest="spectro", default=False, | ||
| 110 | help="add spectrogram to the plot") | ||
| 111 | """ | ||
| 112 | parser.add_option("-o","--output", type = str, | ||
| 113 | metavar = "<outputdir>", | ||
| 114 | action="store", dest="output_directory", default=None, | ||
| 115 | help="specify path where slices of the original file should be created") | ||
| 116 | parser.add_option("--cut-until-nsamples", type = int, | ||
| 117 | metavar = "<samples>", | ||
| 118 | action = "store", dest = "cut_until_nsamples", default = None, | ||
| 119 | help="how many extra samples should be added at the end of each slice") | ||
| 120 | parser.add_option("--cut-until-nslices", type = int, | ||
| 121 | metavar = "<slices>", | ||
| 122 | action = "store", dest = "cut_until_nslices", default = None, | ||
| 123 | help="how many extra slices should be added at the end of each slice") | ||
| 124 | |||
| 125 | parser.add_option("-v","--verbose", | ||
| 126 | action="store_true", dest="verbose", default=True, | ||
| 127 | help="make lots of noise [default]") | ||
| 128 | parser.add_option("-q","--quiet", | ||
| 129 | action="store_false", dest="verbose", default=True, | ||
| 130 | help="be quiet") | ||
| 131 | (options, args) = parser.parse_args() | ||
| 132 | if not options.source_file: | ||
| 133 | import os.path | ||
| 134 | if len(args) == 1: | ||
| 135 | options.source_file = args[0] | ||
| 136 | else: | ||
| 137 | print ("no file name given\n" + usage) | ||
| 138 | sys.exit(1) | ||
| 139 | return options, args | ||
| 140 | |||
| 141 | if __name__ == '__main__': | ||
| 142 | options, args = parse_args() | ||
| 143 | |||
| 144 | hopsize = options.hopsize | ||
| 145 | bufsize = options.bufsize | ||
| 146 | samplerate = options.samplerate | ||
| 147 | source_file = options.source_file | ||
| 148 | |||
| 149 | from aubio import onset, tempo, source, sink | ||
| 150 | |||
| 151 | s = source(source_file, samplerate, hopsize) | ||
| 152 | if samplerate == 0: samplerate = s.get_samplerate() | ||
| 153 | |||
| 154 | if options.beat: | ||
| 155 | o = tempo(options.onset_method, bufsize, hopsize) | ||
| 156 | else: | ||
| 157 | o = onset(options.onset_method, bufsize, hopsize) | ||
| 158 | if options.minioi: | ||
| 159 | if options.minioi.endswith('ms'): | ||
| 160 | o.set_minioi_ms(int(options.minioi[:-2])) | ||
| 161 | elif options.minioi.endswith('s'): | ||
| 162 | o.set_minioi_s(int(options.minioi[:-1])) | ||
| 163 | else: | ||
| 164 | o.set_minioi(int(options.minioi)) | ||
| 165 | o.set_threshold(options.threshold) | ||
| 166 | |||
| 167 | timestamps = [] | ||
| 168 | total_frames = 0 | ||
| 169 | # analyze pass | ||
| 170 | while True: | ||
| 171 | samples, read = s() | ||
| 172 | if o(samples): | ||
| 173 | timestamps.append (o.get_last()) | ||
| 174 | if options.verbose: print ("%.4f" % o.get_last_s()) | ||
| 175 | total_frames += read | ||
| 176 | if read < hopsize: break | ||
| 177 | del s | ||
| 178 | # print some info | ||
| 179 | nstamps = len(timestamps) | ||
| 180 | duration = float (total_frames) / float(samplerate) | ||
| 181 | info = 'found %(nstamps)d timestamps in %(source_file)s' % locals() | ||
| 182 | info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals() | ||
| 183 | sys.stderr.write(info) | ||
| 184 | |||
| 185 | # cutting pass | ||
| 186 | if options.cut and nstamps > 0: | ||
| 187 | # generate output files | ||
| 188 | from aubio.slicing import slice_source_at_stamps | ||
| 189 | timestamps_end = None | ||
| 190 | if options.cut_until_nslices and options.cut_until_nsamples: | ||
| 191 | print ("warning: using cut_until_nslices, but cut_until_nsamples is set") | ||
| 192 | if options.cut_until_nsamples: | ||
| 193 | timestamps_end = [t + options.cut_until_nsamples for t in timestamps[1:]] | ||
| 194 | timestamps_end += [ 1e120 ] | ||
| 195 | if options.cut_until_nslices: | ||
| 196 | timestamps_end = [t for t in timestamps[1 + options.cut_until_nslices:]] | ||
| 197 | timestamps_end += [ 1e120 ] * (options.cut_until_nslices + 1) | ||
| 198 | slice_source_at_stamps(source_file, timestamps, timestamps_end = timestamps_end, | ||
| 199 | output_dir = options.output_directory, | ||
| 200 | samplerate = samplerate) | ||
| 201 | |||
| 202 | # print some info | ||
| 203 | duration = float (total_frames) / float(samplerate) | ||
| 204 | info = 'created %(nstamps)d slices from %(source_file)s' % locals() | ||
| 205 | info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals() | ||
| 206 | sys.stderr.write(info) | ||
| @@ -0,0 +1 @@ | |||
| 1 | |||
| @@ -24,7 +24,7 @@ import sys · 23 unmodified lines | |||
| 24 | import time | 24 | import time |
| 25 | import os.path | 25 | import os.path |
| 26 | import numpy | 26 | import numpy |
| 27 | from utils import array_from_text_file, array_from_yaml_file | 27 | from .utils import array_from_text_file, array_from_yaml_file |
| 28 | from aubio import source, pitch, freqtomidi | 28 | from aubio import source, pitch, freqtomidi |
| 29 | 29 | ||
| 30 | start = time.time() | 30 | start = time.time() |
| @@ -9,6 +9,11 @@ class aubiomodule_test_case(TestCase): · 8 unmodified lines | |||
| 9 | """ try importing aubio """ | 9 | """ try importing aubio """ |
| 10 | import aubio | 10 | import aubio |
| 11 | 11 | ||
| 12 | def test_version(self): | ||
| 13 | """ test aubio.version """ | ||
| 14 | import aubio | ||
| 15 | self.assertEqual('0', aubio.version[0]) | ||
| 16 | |||
| 12 | if __name__ == '__main__': | 17 | if __name__ == '__main__': |
| 13 | main() | 18 | main() |
| 14 | 19 | ||
| @@ -0,0 +1,34 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | import aubio.cmd | ||
| 4 | from nose2 import main | ||
| 5 | from numpy.testing import TestCase | ||
| 6 | |||
| 7 | class aubio_cmd(TestCase): | ||
| 8 | |||
| 9 | def setUp(self): | ||
| 10 | self.a_parser = aubio.cmd.aubio_parser() | ||
| 11 | |||
| 12 | def test_default_creation(self): | ||
| 13 | try: | ||
| 14 | assert self.a_parser.parse_args(['-V']).show_version | ||
| 15 | except SystemExit: | ||
| 16 | url = 'https://bugs.python.org/issue9253' | ||
| 17 | self.skipTest('subcommand became optional in py3, see %s' % url) | ||
| 18 | |||
| 19 | class aubio_cmd_utils(TestCase): | ||
| 20 | |||
| 21 | def test_samples2seconds(self): | ||
| 22 | self.assertEqual(aubio.cmd.samples2seconds(3200, 32000), | ||
| 23 | "0.100000\t") | ||
| 24 | |||
| 25 | def test_samples2milliseconds(self): | ||
| 26 | self.assertEqual(aubio.cmd.samples2milliseconds(3200, 32000), | ||
| 27 | "100.000000\t") | ||
| 28 | |||
| 29 | def test_samples2samples(self): | ||
| 30 | self.assertEqual(aubio.cmd.samples2samples(3200, 32000), | ||
| 31 | "3200\t") | ||
| 32 | |||
| 33 | if __name__ == '__main__': | ||
| 34 | main() | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | import aubio.cut | ||
| 4 | from nose2 import main | ||
| 5 | from numpy.testing import TestCase | ||
| 6 | |||
| 7 | class aubio_cut(TestCase): | ||
| 8 | |||
| 9 | def setUp(self): | ||
| 10 | self.a_parser = aubio.cut.aubio_cut_parser() | ||
| 11 | |||
| 12 | def test_default_creation(self): | ||
| 13 | assert self.a_parser.parse_args(['-v']).verbose | ||
| 14 | |||
| 15 | if __name__ == '__main__': | ||
| 16 | main() | ||
| @@ -0,0 +1,68 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | |||
| 4 | import numpy as np | ||
| 5 | from numpy.testing import TestCase, assert_almost_equal | ||
| 6 | import aubio | ||
| 7 | |||
| 8 | precomputed_arange = [ 9.89949512, -6.44232273, 0., -0.67345482, 0., | ||
| 9 | -0.20090288, 0., -0.05070186] | ||
| 10 | |||
| 11 | precomputed_some_ones = [ 4.28539848, 0.2469689, -0.14625292, -0.58121818, | ||
| 12 | -0.83483052, -0.75921834, -0.35168475, 0.24087936, | ||
| 13 | 0.78539824, 1.06532764, 0.97632152, 0.57164496, 0.03688532, | ||
| 14 | -0.39446154, -0.54619485, -0.37771079] | ||
| 15 | |||
| 16 | class aubio_dct(TestCase): | ||
| 17 | |||
| 18 | def test_init(self): | ||
| 19 | """ test that aubio.dct() is created with expected size """ | ||
| 20 | a_dct = aubio.dct() | ||
| 21 | self.assertEqual(a_dct.size, 1024) | ||
| 22 | |||
| 23 | def test_arange(self): | ||
| 24 | """ test that dct(arange(8)) is computed correctly | ||
| 25 | |||
| 26 | >>> from scipy.fftpack import dct | ||
| 27 | >>> a_in = np.arange(8).astype(aubio.float_type) | ||
| 28 | >>> precomputed = dct(a_in, norm='ortho') | ||
| 29 | """ | ||
| 30 | N = len(precomputed_arange) | ||
| 31 | a_dct = aubio.dct(8) | ||
| 32 | a_in = np.arange(8).astype(aubio.float_type) | ||
| 33 | a_expected = aubio.fvec(precomputed_arange) | ||
| 34 | assert_almost_equal(a_dct(a_in), a_expected, decimal=5) | ||
| 35 | |||
| 36 | def test_some_ones(self): | ||
| 37 | """ test that dct(somevector) is computed correctly """ | ||
| 38 | a_dct = aubio.dct(16) | ||
| 39 | a_in = np.ones(16).astype(aubio.float_type) | ||
| 40 | a_in[1] = 0 | ||
| 41 | a_in[3] = np.pi | ||
| 42 | a_expected = aubio.fvec(precomputed_some_ones) | ||
| 43 | assert_almost_equal(a_dct(a_in), a_expected, decimal=6) | ||
| 44 | |||
| 45 | def test_reconstruction(self): | ||
| 46 | """ test that some_ones vector can be recontructed """ | ||
| 47 | a_dct = aubio.dct(16) | ||
| 48 | a_in = np.ones(16).astype(aubio.float_type) | ||
| 49 | a_in[1] = 0 | ||
| 50 | a_in[3] = np.pi | ||
| 51 | a_dct_in = a_dct(a_in) | ||
| 52 | a_dct_reconstructed = a_dct.rdo(a_dct_in) | ||
| 53 | assert_almost_equal(a_dct_reconstructed, a_in, decimal=6) | ||
| 54 | |||
| 55 | def test_negative_size(self): | ||
| 56 | """ test that creation fails with a negative size """ | ||
| 57 | with self.assertRaises(ValueError): | ||
| 58 | aubio.dct(-1) | ||
| 59 | |||
| 60 | def test_wrong_size(self): | ||
| 61 | """ test that creation fails with a non power-of-two size """ | ||
| 62 | # supports for non 2** fft sizes only when compiled with fftw3 | ||
| 63 | size = 13 | ||
| 64 | try: | ||
| 65 | with self.assertRaises(RuntimeError): | ||
| 66 | aubio.dct(size) | ||
| 67 | except AssertionError: | ||
| 68 | self.skipTest('creating aubio.dct with size %d did not fail' % size) | ||
| @@ -33,7 +33,14 @@ class aubio_fft_test_case(TestCase): · 32 unmodified lines | |||
| 33 | f = fft (win_s) | 33 | f = fft (win_s) |
| 34 | fftgrain = f (timegrain) | 34 | fftgrain = f (timegrain) |
| 35 | assert_equal ( fftgrain.norm, 0 ) | 35 | assert_equal ( fftgrain.norm, 0 ) |
| 36 | assert_equal ( fftgrain.phas, 0 ) | 36 | try: |
| 37 | assert_equal ( fftgrain.phas, 0 ) | ||
| 38 | except AssertionError: | ||
| 39 | assert_equal (fftgrain.phas[fftgrain.phas > 0], +pi) | ||
| 40 | assert_equal (fftgrain.phas[fftgrain.phas < 0], -pi) | ||
| 41 | assert_equal (np.abs(fftgrain.phas[np.abs(fftgrain.phas) != pi]), 0) | ||
| 42 | self.skipTest('fft(fvec(%d)).phas != +0, ' % win_s \ | ||
| 43 | + 'This is expected when using fftw3 on powerpc.') | ||
| 37 | 44 | ||
| 38 | def test_impulse(self): | 45 | def test_impulse(self): |
| 39 | """ check the transform of one impulse at a random place """ | 46 | """ check the transform of one impulse at a random place """ |
| @@ -135,6 +142,37 @@ class aubio_fft_test_case(TestCase): · 95 unmodified lines | |||
| 135 | assert_almost_equal ( r[0], impulse, decimal = 6) | 142 | assert_almost_equal ( r[0], impulse, decimal = 6) |
| 136 | assert_almost_equal ( r[1:], 0) | 143 | assert_almost_equal ( r[1:], 0) |
| 137 | 144 | ||
| 145 | class aubio_fft_odd_sizes(TestCase): | ||
| 146 | |||
| 147 | def test_reconstruct_with_odd_size(self): | ||
| 148 | win_s = 29 | ||
| 149 | self.recontruct(win_s, 'odd sizes not supported') | ||
| 150 | |||
| 151 | def test_reconstruct_with_radix15(self): | ||
| 152 | win_s = 2 ** 4 * 15 | ||
| 153 | self.recontruct(win_s, 'radix 15 supported') | ||
| 154 | |||
| 155 | def test_reconstruct_with_radix5(self): | ||
| 156 | win_s = 2 ** 4 * 5 | ||
| 157 | self.recontruct(win_s, 'radix 5 supported') | ||
| 158 | |||
| 159 | def test_reconstruct_with_radix3(self): | ||
| 160 | win_s = 2 ** 4 * 3 | ||
| 161 | self.recontruct(win_s, 'radix 3 supported') | ||
| 162 | |||
| 163 | def recontruct(self, win_s, skipMessage): | ||
| 164 | try: | ||
| 165 | f = fft(win_s) | ||
| 166 | except RuntimeError: | ||
| 167 | self.skipTest(skipMessage) | ||
| 168 | input_signal = fvec(win_s) | ||
| 169 | input_signal[win_s//2] = 1 | ||
| 170 | c = f(input_signal) | ||
| 171 | output_signal = f.rdo(c) | ||
| 172 | assert_almost_equal(input_signal, output_signal) | ||
| 173 | |||
| 174 | class aubio_fft_wrong_params(TestCase): | ||
| 175 | |||
| 138 | def test_large_input_timegrain(self): | 176 | def test_large_input_timegrain(self): |
| 139 | win_s = 1024 | 177 | win_s = 1024 |
| 140 | f = fft(win_s) | 178 | f = fft(win_s) |
| @@ -163,22 +201,11 @@ class aubio_fft_test_case(TestCase): · 22 unmodified lines | |||
| 163 | with self.assertRaises(ValueError): | 201 | with self.assertRaises(ValueError): |
| 164 | f.rdo(s) | 202 | f.rdo(s) |
| 165 | 203 | ||
| 166 | class aubio_fft_wrong_params(TestCase): | ||
| 167 | |||
| 168 | def test_wrong_buf_size(self): | 204 | def test_wrong_buf_size(self): |
| 169 | win_s = -1 | 205 | win_s = -1 |
| 170 | with self.assertRaises(ValueError): | 206 | with self.assertRaises(ValueError): |
| 171 | fft(win_s) | 207 | fft(win_s) |
| 172 | 208 | ||
| 173 | def test_buf_size_not_power_of_two(self): | ||
| 174 | # when compiled with fftw3, aubio supports non power of two fft sizes | ||
| 175 | win_s = 320 | ||
| 176 | try: | ||
| 177 | with self.assertRaises(RuntimeError): | ||
| 178 | fft(win_s) | ||
| 179 | except AssertionError: | ||
| 180 | self.skipTest('creating aubio.fft with size %d did not fail' % win_s) | ||
| 181 | |||
| 182 | def test_buf_size_too_small(self): | 209 | def test_buf_size_too_small(self): |
| 183 | win_s = 1 | 210 | win_s = 1 |
| 184 | with self.assertRaises(RuntimeError): | 211 | with self.assertRaises(RuntimeError): |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | |||
| 3 | from unittest import main | 3 | from unittest import main |
| 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal | 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal |
| 5 | from aubio import fvec, digital_filter | 5 | from aubio import fvec, digital_filter |
| 6 | from utils import array_from_text_file | 6 | from .utils import array_from_text_file |
| 7 | 7 | ||
| 8 | class aubio_filter_test_case(TestCase): | 8 | class aubio_filter_test_case(TestCase): |
| 9 | 9 | ||
| @@ -77,6 +77,16 @@ class aubio_filter_test_case(TestCase): · 67 unmodified lines | |||
| 77 | with self.assertRaises(ValueError): | 77 | with self.assertRaises(ValueError): |
| 78 | f.set_biquad(0., 0., 0, 0., 0.) | 78 | f.set_biquad(0., 0., 0, 0., 0.) |
| 79 | 79 | ||
| 80 | def test_all_available_presets(self): | ||
| 81 | f = digital_filter(7) | ||
| 82 | for sr in [8000, 11025, 16000, 22050, 24000, 32000, | ||
| 83 | 44100, 48000, 88200, 96000, 192000]: | ||
| 84 | f.set_a_weighting(sr) | ||
| 85 | f = digital_filter(5) | ||
| 86 | for sr in [8000, 11025, 16000, 22050, 24000, 32000, | ||
| 87 | 44100, 48000, 88200, 96000, 192000]: | ||
| 88 | f.set_c_weighting(sr) | ||
| 89 | |||
| 80 | class aubio_filter_wrong_params(TestCase): | 90 | class aubio_filter_wrong_params(TestCase): |
| 81 | 91 | ||
| 82 | def test_negative_order(self): | 92 | def test_negative_order(self): |
| @@ -1,11 +1,10 @@ | |||
| 1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
| 2 | 2 | ||
| 3 | from unittest import main | ||
| 4 | from numpy.testing import TestCase | ||
| 5 | from numpy.testing import assert_equal, assert_almost_equal | ||
| 6 | import numpy as np | 3 | import numpy as np |
| 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal | ||
| 5 | |||
| 7 | from aubio import cvec, filterbank, float_type | 6 | from aubio import cvec, filterbank, float_type |
| 8 | from utils import array_from_text_file | 7 | from .utils import array_from_text_file |
| 9 | 8 | ||
| 10 | class aubio_filterbank_test_case(TestCase): | 9 | class aubio_filterbank_test_case(TestCase): |
| 11 | 10 | ||
| @@ -62,6 +61,13 @@ class aubio_filterbank_test_case(TestCase): · 50 unmodified lines | |||
| 62 | f.set_mel_coeffs_slaney(16000) | 61 | f.set_mel_coeffs_slaney(16000) |
| 63 | assert_almost_equal ( expected, f.get_coeffs() ) | 62 | assert_almost_equal ( expected, f.get_coeffs() ) |
| 64 | 63 | ||
| 64 | def test_mfcc_coeffs_get_coeffs(self): | ||
| 65 | f = filterbank(40, 512) | ||
| 66 | coeffs = f.get_coeffs() | ||
| 67 | self.assertIsInstance(coeffs, np.ndarray) | ||
| 68 | assert_equal (coeffs, 0) | ||
| 69 | assert_equal (np.shape(coeffs), (40, 512 / 2 + 1)) | ||
| 70 | |||
| 65 | class aubio_filterbank_wrong_values(TestCase): | 71 | class aubio_filterbank_wrong_values(TestCase): |
| 66 | 72 | ||
| 67 | def test_negative_window(self): | 73 | def test_negative_window(self): |
| @@ -81,4 +87,5 @@ class aubio_filterbank_wrong_values(TestCase): · 13 unmodified lines | |||
| 81 | f(cvec(256)) | 87 | f(cvec(256)) |
| 82 | 88 | ||
| 83 | if __name__ == '__main__': | 89 | if __name__ == '__main__': |
| 84 | main() | 90 | import nose2 |
| 91 | nose2.main() | ||
| @@ -1,10 +1,12 @@ | |||
| 1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
| 2 | 2 | ||
| 3 | from unittest import main | 3 | import numpy as np |
| 4 | from numpy.testing import TestCase | 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal |
| 5 | from numpy.testing import assert_equal, assert_almost_equal | 5 | |
| 6 | from numpy import array, shape | 6 | from aubio import fvec, cvec, filterbank, float_type |
| 7 | from aubio import cvec, filterbank, float_type | 7 | |
| 8 | import warnings | ||
| 9 | warnings.filterwarnings('ignore', category=UserWarning, append=True) | ||
| 8 | 10 | ||
| 9 | class aubio_filterbank_mel_test_case(TestCase): | 11 | class aubio_filterbank_mel_test_case(TestCase): |
| 10 | 12 | ||
| @@ -12,38 +14,159 @@ class aubio_filterbank_mel_test_case(TestCase): · 1 unmodified line | |||
| 12 | f = filterbank(40, 512) | 14 | f = filterbank(40, 512) |
| 13 | f.set_mel_coeffs_slaney(16000) | 15 | f.set_mel_coeffs_slaney(16000) |
| 14 | a = f.get_coeffs() | 16 | a = f.get_coeffs() |
| 15 | assert_equal(shape (a), (40, 512/2 + 1) ) | 17 | assert_equal(np.shape (a), (40, 512/2 + 1) ) |
| 16 | 18 | ||
| 17 | def test_other_slaney(self): | 19 | def test_other_slaney(self): |
| 18 | f = filterbank(40, 512*2) | 20 | f = filterbank(40, 512*2) |
| 19 | f.set_mel_coeffs_slaney(44100) | 21 | f.set_mel_coeffs_slaney(44100) |
| 20 | _ = f.get_coeffs() | 22 | self.assertIsInstance(f.get_coeffs(), np.ndarray) |
| 21 | #print "sum is", sum(sum(a)) | 23 | #print "sum is", sum(sum(a)) |
| 22 | for win_s in [256, 512, 1024, 2048, 4096]: | 24 | for win_s in [256, 512, 1024, 2048, 4096]: |
| 23 | f = filterbank(40, win_s) | 25 | f = filterbank(40, win_s) |
| 24 | f.set_mel_coeffs_slaney(32000) | 26 | f.set_mel_coeffs_slaney(32000) |
| 25 | _ = f.get_coeffs() | ||
| 26 | #print "sum is", sum(sum(a)) | 27 | #print "sum is", sum(sum(a)) |
| 28 | self.assertIsInstance(f.get_coeffs(), np.ndarray) | ||
| 27 | 29 | ||
| 28 | def test_triangle_freqs_zeros(self): | 30 | def test_triangle_freqs_zeros(self): |
| 29 | f = filterbank(9, 1024) | 31 | f = filterbank(9, 1024) |
| 30 | freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] | 32 | freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] |
| 31 | freqs = array(freq_list, dtype = float_type) | 33 | freqs = np.array(freq_list, dtype = float_type) |
| 32 | f.set_triangle_bands(freqs, 48000) | 34 | f.set_triangle_bands(freqs, 48000) |
| 33 | _ = f.get_coeffs().T | ||
| 34 | assert_equal ( f(cvec(1024)), 0) | 35 | assert_equal ( f(cvec(1024)), 0) |
| 36 | self.assertIsInstance(f.get_coeffs(), np.ndarray) | ||
| 35 | 37 | ||
| 36 | def test_triangle_freqs_ones(self): | 38 | def test_triangle_freqs_ones(self): |
| 37 | f = filterbank(9, 1024) | 39 | f = filterbank(9, 1024) |
| 38 | freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] | 40 | freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] |
| 39 | freqs = array(freq_list, dtype = float_type) | 41 | freqs = np.array(freq_list, dtype = float_type) |
| 40 | f.set_triangle_bands(freqs, 48000) | 42 | f.set_triangle_bands(freqs, 48000) |
| 41 | _ = f.get_coeffs().T | 43 | self.assertIsInstance(f.get_coeffs(), np.ndarray) |
| 42 | spec = cvec(1024) | 44 | spec = cvec(1024) |
| 43 | spec.norm[:] = 1 | 45 | spec.norm[:] = 1 |
| 44 | assert_almost_equal ( f(spec), | 46 | assert_almost_equal ( f(spec), |
| 45 | [ 0.02070313, 0.02138672, 0.02127604, 0.02135417, | 47 | [ 0.02070313, 0.02138672, 0.02127604, 0.02135417, |
| 46 | 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345]) | 48 | 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345]) |
| 47 | 49 | ||
| 50 | def test_triangle_freqs_with_zeros(self): | ||
| 51 | """make sure set_triangle_bands works when list starts with 0""" | ||
| 52 | freq_list = [0, 40, 80] | ||
| 53 | freqs = np.array(freq_list, dtype = float_type) | ||
| 54 | f = filterbank(len(freqs)-2, 1024) | ||
| 55 | f.set_triangle_bands(freqs, 48000) | ||
| 56 | assert_equal ( f(cvec(1024)), 0) | ||
| 57 | self.assertIsInstance(f.get_coeffs(), np.ndarray) | ||
| 58 | |||
| 59 | def test_triangle_freqs_with_wrong_negative(self): | ||
| 60 | """make sure set_triangle_bands fails when list contains a negative""" | ||
| 61 | freq_list = [-10, 0, 80] | ||
| 62 | f = filterbank(len(freq_list)-2, 1024) | ||
| 63 | with self.assertRaises(ValueError): | ||
| 64 | f.set_triangle_bands(fvec(freq_list), 48000) | ||
| 65 | |||
| 66 | def test_triangle_freqs_with_wrong_ordering(self): | ||
| 67 | """make sure set_triangle_bands fails when list not ordered""" | ||
| 68 | freq_list = [0, 80, 40] | ||
| 69 | f = filterbank(len(freq_list)-2, 1024) | ||
| 70 | with self.assertRaises(ValueError): | ||
| 71 | f.set_triangle_bands(fvec(freq_list), 48000) | ||
| 72 | |||
| 73 | def test_triangle_freqs_with_large_freq(self): | ||
| 74 | """make sure set_triangle_bands warns when freq > nyquist""" | ||
| 75 | samplerate = 22050 | ||
| 76 | freq_list = [0, samplerate//4, samplerate // 2 + 1] | ||
| 77 | f = filterbank(len(freq_list)-2, 1024) | ||
| 78 | # TODO add assert_warns | ||
| 79 | f.set_triangle_bands(fvec(freq_list), samplerate) | ||
| 80 | |||
| 81 | def test_triangle_freqs_with_not_enough_filters(self): | ||
| 82 | """make sure set_triangle_bands warns when not enough filters""" | ||
| 83 | samplerate = 22050 | ||
| 84 | freq_list = [0, 100, 1000, 4000, 8000, 10000] | ||
| 85 | f = filterbank(len(freq_list)-3, 1024) | ||
| 86 | # TODO add assert_warns | ||
| 87 | f.set_triangle_bands(fvec(freq_list), samplerate) | ||
| 88 | |||
| 89 | def test_triangle_freqs_with_too_many_filters(self): | ||
| 90 | """make sure set_triangle_bands warns when too many filters""" | ||
| 91 | samplerate = 22050 | ||
| 92 | freq_list = [0, 100, 1000, 4000, 8000, 10000] | ||
| 93 | f = filterbank(len(freq_list)-1, 1024) | ||
| 94 | # TODO add assert_warns | ||
| 95 | f.set_triangle_bands(fvec(freq_list), samplerate) | ||
| 96 | |||
| 97 | def test_triangle_freqs_with_double_value(self): | ||
| 98 | """make sure set_triangle_bands works with 2 duplicate freqs""" | ||
| 99 | samplerate = 22050 | ||
| 100 | freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000] | ||
| 101 | f = filterbank(len(freq_list)-2, 1024) | ||
| 102 | # TODO add assert_warns | ||
| 103 | f.set_triangle_bands(fvec(freq_list), samplerate) | ||
| 104 | |||
| 105 | def test_triangle_freqs_with_triple(self): | ||
| 106 | """make sure set_triangle_bands works with 3 duplicate freqs""" | ||
| 107 | samplerate = 22050 | ||
| 108 | freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000] | ||
| 109 | f = filterbank(len(freq_list)-2, 1024) | ||
| 110 | # TODO add assert_warns | ||
| 111 | f.set_triangle_bands(fvec(freq_list), samplerate) | ||
| 112 | |||
| 113 | def test_triangle_freqs_without_norm(self): | ||
| 114 | """make sure set_triangle_bands works without """ | ||
| 115 | samplerate = 22050 | ||
| 116 | freq_list = fvec([0, 100, 1000, 10000]) | ||
| 117 | f = filterbank(len(freq_list) - 2, 1024) | ||
| 118 | f.set_norm(0) | ||
| 119 | f.set_triangle_bands(freq_list, samplerate) | ||
| 120 | expected = f.get_coeffs() | ||
| 121 | f.set_norm(1) | ||
| 122 | f.set_triangle_bands(fvec(freq_list), samplerate) | ||
| 123 | assert_almost_equal(f.get_coeffs().T, | ||
| 124 | expected.T * 2. / (freq_list[2:] - freq_list[:-2])) | ||
| 125 | |||
| 126 | def test_triangle_freqs_wrong_norm(self): | ||
| 127 | f = filterbank(10, 1024) | ||
| 128 | with self.assertRaises(ValueError): | ||
| 129 | f.set_norm(-1) | ||
| 130 | |||
| 131 | def test_triangle_freqs_with_power(self): | ||
| 132 | f = filterbank(9, 1024) | ||
| 133 | freqs = fvec([40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, | ||
| 134 | 24000]) | ||
| 135 | f.set_power(2) | ||
| 136 | f.set_triangle_bands(freqs, 48000) | ||
| 137 | spec = cvec(1024) | ||
| 138 | spec.norm[:] = .1 | ||
| 139 | expected = fvec([0.02070313, 0.02138672, 0.02127604, 0.02135417, | ||
| 140 | 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345]) | ||
| 141 | expected /= 100. | ||
| 142 | assert_almost_equal(f(spec), expected) | ||
| 143 | |||
| 144 | def test_mel_coeffs(self): | ||
| 145 | f = filterbank(40, 1024) | ||
| 146 | f.set_mel_coeffs(44100, 0, 44100 / 2) | ||
| 147 | |||
| 148 | def test_zero_fmax(self): | ||
| 149 | f = filterbank(40, 1024) | ||
| 150 | f.set_mel_coeffs(44100, 0, 0) | ||
| 151 | |||
| 152 | def test_wrong_mel_coeffs(self): | ||
| 153 | f = filterbank(40, 1024) | ||
| 154 | with self.assertRaises(ValueError): | ||
| 155 | f.set_mel_coeffs_slaney(0) | ||
| 156 | with self.assertRaises(ValueError): | ||
| 157 | f.set_mel_coeffs(44100, 0, -44100 / 2) | ||
| 158 | with self.assertRaises(ValueError): | ||
| 159 | f.set_mel_coeffs(44100, -0.1, 44100 / 2) | ||
| 160 | with self.assertRaises(ValueError): | ||
| 161 | f.set_mel_coeffs(-44100, 0.1, 44100 / 2) | ||
| 162 | with self.assertRaises(ValueError): | ||
| 163 | f.set_mel_coeffs_htk(-1, 0, 0) | ||
| 164 | |||
| 165 | def test_mel_coeffs_htk(self): | ||
| 166 | f = filterbank(40, 1024) | ||
| 167 | f.set_mel_coeffs_htk(44100, 0, 44100 / 2) | ||
| 168 | |||
| 169 | |||
| 48 | if __name__ == '__main__': | 170 | if __name__ == '__main__': |
| 49 | main() | 171 | import nose2 |
| 172 | nose2.main() | ||
| @@ -60,6 +60,14 @@ class aubio_fvec_wrong_values(TestCase): · 59 unmodified lines | |||
| 60 | self.assertRaises(IndexError, a.__getitem__, 3) | 60 | self.assertRaises(IndexError, a.__getitem__, 3) |
| 61 | self.assertRaises(IndexError, a.__getitem__, 2) | 61 | self.assertRaises(IndexError, a.__getitem__, 2) |
| 62 | 62 | ||
| 63 | def test_wrong_dimensions(self): | ||
| 64 | a = np.array([[[1, 2], [3, 4]]], dtype=float_type) | ||
| 65 | self.assertRaises(ValueError, fvec, a) | ||
| 66 | |||
| 67 | def test_wrong_size(self): | ||
| 68 | a = np.ndarray([0,], dtype=float_type) | ||
| 69 | self.assertRaises(ValueError, fvec, a) | ||
| 70 | |||
| 63 | class aubio_wrong_fvec_input(TestCase): | 71 | class aubio_wrong_fvec_input(TestCase): |
| 64 | """ uses min_removal to test PyAubio_IsValidVector """ | 72 | """ uses min_removal to test PyAubio_IsValidVector """ |
| 65 | 73 | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | import numpy as np | ||
| 4 | from numpy.testing import TestCase, assert_equal | ||
| 5 | import aubio | ||
| 6 | |||
| 7 | class aubio_shift_test_case(TestCase): | ||
| 8 | |||
| 9 | def run_shift_ishift(self, n): | ||
| 10 | ramp = np.arange(n, dtype=aubio.float_type) | ||
| 11 | # construct expected output | ||
| 12 | # even length: [5. 6. 7. 8. 9. 0. 1. 2. 3. 4.] | ||
| 13 | # odd length: [4. 5. 6. 0. 1. 2. 3.] | ||
| 14 | half = n - n//2 | ||
| 15 | expected = np.concatenate([np.arange(half, n), np.arange(half)]) | ||
| 16 | # shift in place, returns modified copy | ||
| 17 | assert_equal(aubio.shift(ramp), expected) | ||
| 18 | # check input was changed as expected | ||
| 19 | assert_equal(ramp, expected) | ||
| 20 | # construct expected output | ||
| 21 | expected = np.arange(n) | ||
| 22 | # revert shift in place, returns modifed copy | ||
| 23 | assert_equal(aubio.ishift(ramp), expected) | ||
| 24 | # check input was shifted back | ||
| 25 | assert_equal(ramp, expected) | ||
| 26 | |||
| 27 | def test_can_shift_fvec(self): | ||
| 28 | self.run_shift_ishift(10) | ||
| 29 | |||
| 30 | def test_can_shift_fvec_odd(self): | ||
| 31 | self.run_shift_ishift(7) | ||
| 32 | |||
| 33 | from unittest import main | ||
| 34 | if __name__ == '__main__': | ||
| 35 | main() | ||
| @@ -0,0 +1,109 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | from unittest import main | ||
| 4 | from numpy.testing import TestCase | ||
| 5 | from numpy.testing import assert_equal, assert_almost_equal | ||
| 6 | import numpy as np | ||
| 7 | import aubio | ||
| 8 | |||
| 9 | from aubio import hztomel, meltohz | ||
| 10 | from aubio import hztomel_htk, meltohz_htk | ||
| 11 | |||
| 12 | |||
| 13 | class aubio_hztomel_test_case(TestCase): | ||
| 14 | |||
| 15 | def test_hztomel(self): | ||
| 16 | assert_equal(hztomel(0.), 0.) | ||
| 17 | assert_almost_equal(hztomel(400. / 3.), 2., decimal=5) | ||
| 18 | assert_almost_equal(hztomel(1000. / 3), 5.) | ||
| 19 | assert_equal(hztomel(200.), 3.) | ||
| 20 | assert_almost_equal(hztomel(1000.), 15) | ||
| 21 | assert_almost_equal(hztomel(6400), 42) | ||
| 22 | assert_almost_equal(hztomel(40960), 69) | ||
| 23 | |||
| 24 | for m in np.linspace(0, 1000, 100): | ||
| 25 | assert_almost_equal(hztomel(meltohz(m)) - m, 0, decimal=3) | ||
| 26 | |||
| 27 | def test_meltohz(self): | ||
| 28 | assert_equal(meltohz(0.), 0.) | ||
| 29 | assert_almost_equal(meltohz(2), 400. / 3., decimal=4) | ||
| 30 | assert_equal(meltohz(3.), 200.) | ||
| 31 | assert_almost_equal(meltohz(5), 1000. / 3., decimal=4) | ||
| 32 | assert_almost_equal(meltohz(15), 1000., decimal=4) | ||
| 33 | assert_almost_equal(meltohz(42), 6400., decimal=2) | ||
| 34 | assert_almost_equal(meltohz(69), 40960., decimal=1) | ||
| 35 | |||
| 36 | for f in np.linspace(0, 20000, 1000): | ||
| 37 | assert_almost_equal(meltohz(hztomel(f)) - f, 0, decimal=1) | ||
| 38 | |||
| 39 | def test_meltohz_negative(self): | ||
| 40 | # TODO add assert_warns | ||
| 41 | assert_equal(meltohz(-1), 0) | ||
| 42 | |||
| 43 | def test_hztomel_negative(self): | ||
| 44 | # TODO add assert_warns | ||
| 45 | assert_equal(hztomel(-1), 0) | ||
| 46 | |||
| 47 | |||
| 48 | class aubio_hztomel_htk_test_case(TestCase): | ||
| 49 | |||
| 50 | def test_meltohz(self): | ||
| 51 | assert_equal(meltohz(0, htk=True), 0) | ||
| 52 | assert_almost_equal(meltohz(2595, htk=True), 6300., decimal=1) | ||
| 53 | |||
| 54 | def test_hztomel(self): | ||
| 55 | assert_equal(hztomel(0, htk=True), 0) | ||
| 56 | assert_almost_equal(hztomel(3428.7, htk=True), 2000., decimal=1) | ||
| 57 | assert_almost_equal(hztomel(6300, htk=True), 2595., decimal=1) | ||
| 58 | |||
| 59 | def test_meltohz_negative(self): | ||
| 60 | # TODO add assert_warns | ||
| 61 | assert_equal(meltohz(-1, htk=True), 0) | ||
| 62 | assert_almost_equal(meltohz(2000, htk=True), 3428.7, decimal=1) | ||
| 63 | assert_almost_equal(meltohz(1000, htk=True), 1000., decimal=1) | ||
| 64 | |||
| 65 | def test_hztomel_negative(self): | ||
| 66 | # TODO add assert_warns | ||
| 67 | assert_equal(hztomel(-1, htk=True), 0) | ||
| 68 | assert_almost_equal(hztomel(1000, htk=True), 1000., decimal=1) | ||
| 69 | |||
| 70 | def test_hztomel_htk(self): | ||
| 71 | for f in np.linspace(0, 20000, 1000): | ||
| 72 | assert_almost_equal(meltohz_htk(hztomel_htk(f)) - f, 0, decimal=1) | ||
| 73 | for f in np.linspace(0, 20000, 1000): | ||
| 74 | assert_almost_equal(hztomel_htk(meltohz_htk(f)) - f, 0, decimal=1) | ||
| 75 | |||
| 76 | |||
| 77 | class aubio_hztomel_wrong_values(TestCase): | ||
| 78 | """ more tests to cover all branches """ | ||
| 79 | |||
| 80 | def test_hztomel_wrong_values(self): | ||
| 81 | with self.assertRaises(TypeError): | ||
| 82 | hztomel('s') | ||
| 83 | |||
| 84 | def test_meltohz_wrong_values(self): | ||
| 85 | with self.assertRaises(TypeError): | ||
| 86 | meltohz(bytes('ad')) | ||
| 87 | |||
| 88 | def test_meltohz_no_arg(self): | ||
| 89 | with self.assertRaises(TypeError): | ||
| 90 | meltohz() | ||
| 91 | |||
| 92 | def test_meltohz_htk_no_arg(self): | ||
| 93 | with self.assertRaises(TypeError): | ||
| 94 | meltohz_htk() | ||
| 95 | |||
| 96 | def test_hztomel_htk_wrong_values(self): | ||
| 97 | with self.assertRaises(TypeError): | ||
| 98 | hztomel_htk('0') | ||
| 99 | |||
| 100 | def test_hztomel_htk_false(self): | ||
| 101 | assert hztomel(120, htk=False) == hztomel(120) | ||
| 102 | |||
| 103 | def test_meltohz_htk_false(self): | ||
| 104 | assert meltohz(12, htk=False) == meltohz(12) | ||
| 105 | |||
| 106 | |||
| 107 | if __name__ == '__main__': | ||
| 108 | from unittest import main | ||
| 109 | main() | ||
| @@ -110,5 +110,42 @@ class aubio_mfcc_all_parameters(TestCase): · 109 unmodified lines | |||
| 110 | o(spec) | 110 | o(spec) |
| 111 | #print coeffs | 111 | #print coeffs |
| 112 | 112 | ||
| 113 | |||
| 114 | class aubio_mfcc_fb_params(TestCase): | ||
| 115 | |||
| 116 | def test_set_scale(self): | ||
| 117 | buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000 | ||
| 118 | m = mfcc(buf_size, n_filters, n_coeffs, samplerate) | ||
| 119 | m.set_scale(10.5) | ||
| 120 | assert m.get_scale() == 10.5 | ||
| 121 | m(cvec(buf_size)) | ||
| 122 | |||
| 123 | def test_set_power(self): | ||
| 124 | buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000 | ||
| 125 | m = mfcc(buf_size, n_filters, n_coeffs, samplerate) | ||
| 126 | m.set_power(2.5) | ||
| 127 | assert m.get_power() == 2.5 | ||
| 128 | m(cvec(buf_size)) | ||
| 129 | |||
| 130 | def test_set_mel_coeffs(self): | ||
| 131 | buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000 | ||
| 132 | m = mfcc(buf_size, n_filters, n_coeffs, samplerate) | ||
| 133 | m.set_mel_coeffs(0., samplerate/2.) | ||
| 134 | m(cvec(buf_size)) | ||
| 135 | |||
| 136 | def test_set_mel_coeffs_htk(self): | ||
| 137 | buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000 | ||
| 138 | m = mfcc(buf_size, n_filters, n_coeffs, samplerate) | ||
| 139 | m.set_mel_coeffs_htk(0., samplerate/2.) | ||
| 140 | m(cvec(buf_size)) | ||
| 141 | |||
| 142 | def test_set_mel_coeffs_slaney(self): | ||
| 143 | buf_size, n_filters, n_coeffs, samplerate = 512, 40, 10, 16000 | ||
| 144 | m = mfcc(buf_size, n_filters, n_coeffs, samplerate) | ||
| 145 | m.set_mel_coeffs_slaney() | ||
| 146 | m(cvec(buf_size)) | ||
| 147 | assert m.get_power() == 1 | ||
| 148 | assert m.get_scale() == 1 | ||
| 149 | |||
| 113 | if __name__ == '__main__': | 150 | if __name__ == '__main__': |
| 114 | main() | 151 | main() |
| @@ -2,6 +2,7 @@ · 1 unmodified line | |||
| 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
| 3 | 3 | ||
| 4 | from aubio import midi2note | 4 | from aubio import midi2note |
| 5 | from nose2.tools import params | ||
| 5 | import unittest | 6 | import unittest |
| 6 | 7 | ||
| 7 | list_of_known_midis = ( | 8 | list_of_known_midis = ( |
| @@ -16,10 +17,10 @@ list_of_known_midis = ( · 8 unmodified lines | |||
| 16 | 17 | ||
| 17 | class midi2note_good_values(unittest.TestCase): | 18 | class midi2note_good_values(unittest.TestCase): |
| 18 | 19 | ||
| 19 | def test_midi2note_known_values(self): | 20 | @params(*list_of_known_midis) |
| 21 | def test_midi2note_known_values(self, midi, note): | ||
| 20 | " known values are correctly converted " | 22 | " known values are correctly converted " |
| 21 | for midi, note in list_of_known_midis: | 23 | self.assertEqual ( midi2note(midi), note ) |
| 22 | self.assertEqual ( midi2note(midi), note ) | ||
| 23 | 24 | ||
| 24 | class midi2note_wrong_values(unittest.TestCase): | 25 | class midi2note_wrong_values(unittest.TestCase): |
| 25 | 26 | ||
| @@ -40,4 +41,5 @@ class midi2note_wrong_values(unittest.TestCase): · 14 unmodified lines | |||
| 40 | self.assertRaises(TypeError, midi2note, "a") | 41 | self.assertRaises(TypeError, midi2note, "a") |
| 41 | 42 | ||
| 42 | if __name__ == '__main__': | 43 | if __name__ == '__main__': |
| 43 | unittest.main() | 44 | import nose2 |
| 45 | nose2.main() | ||
| @@ -3,7 +3,8 @@ · 2 unmodified lines | |||
| 3 | 3 | ||
| 4 | from __future__ import unicode_literals | 4 | from __future__ import unicode_literals |
| 5 | 5 | ||
| 6 | from aubio import note2midi, freq2note | 6 | from aubio import note2midi, freq2note, note2freq, float_type |
| 7 | from nose2.tools import params | ||
| 7 | import unittest | 8 | import unittest |
| 8 | 9 | ||
| 9 | list_of_known_notes = ( | 10 | list_of_known_notes = ( |
| @@ -13,24 +14,57 @@ list_of_known_notes = ( · 3 unmodified lines | |||
| 13 | ( 'C3', 48 ), | 14 | ( 'C3', 48 ), |
| 14 | ( 'B3', 59 ), | 15 | ( 'B3', 59 ), |
| 15 | ( 'B#3', 60 ), | 16 | ( 'B#3', 60 ), |
| 17 | ( 'C♯4', 61 ), | ||
| 16 | ( 'A4', 69 ), | 18 | ( 'A4', 69 ), |
| 17 | ( 'A#4', 70 ), | 19 | ( 'A#4', 70 ), |
| 20 | ( 'A♯4', 70 ), | ||
| 21 | ( 'A\u266f4', 70 ), | ||
| 18 | ( 'Bb4', 70 ), | 22 | ( 'Bb4', 70 ), |
| 19 | ( 'B♭4', 70 ), | 23 | ( 'B♭4', 70 ), |
| 24 | ( 'B\u266d4', 70 ), | ||
| 20 | ( 'G8', 115 ), | 25 | ( 'G8', 115 ), |
| 21 | ( 'G♯8', 116 ), | 26 | ( 'G♯8', 116 ), |
| 22 | ( 'G9', 127 ), | 27 | ( 'G9', 127 ), |
| 23 | ( 'G\udd2a2', 45 ), | ||
| 24 | ( 'B\ufffd2', 45 ), | ||
| 25 | ( 'A♮2', 45 ), | 28 | ( 'A♮2', 45 ), |
| 26 | ) | 29 | ) |
| 27 | 30 | ||
| 31 | list_of_known_notes_with_unicode_issues = ( | ||
| 32 | ('C𝄪4', 62 ), | ||
| 33 | ('E𝄫4', 62 ), | ||
| 34 | ) | ||
| 35 | |||
| 36 | list_of_unknown_notes = ( | ||
| 37 | ( 'G\udd2a2' ), | ||
| 38 | ( 'B\ufffd2' ), | ||
| 39 | ( 'B\u266e\u266e2' ), | ||
| 40 | ( 'B\u266f\u266d3' ), | ||
| 41 | ( 'B33' ), | ||
| 42 | ( 'C.3' ), | ||
| 43 | ( 'A' ), | ||
| 44 | ( '2' ), | ||
| 45 | ) | ||
| 46 | |||
| 28 | class note2midi_good_values(unittest.TestCase): | 47 | class note2midi_good_values(unittest.TestCase): |
| 29 | 48 | ||
| 30 | def test_note2midi_known_values(self): | 49 | @params(*list_of_known_notes) |
| 50 | def test_note2midi_known_values(self, note, midi): | ||
| 31 | " known values are correctly converted " | 51 | " known values are correctly converted " |
| 32 | for note, midi in list_of_known_notes: | 52 | self.assertEqual ( note2midi(note), midi ) |
| 53 | |||
| 54 | @params(*list_of_known_notes_with_unicode_issues) | ||
| 55 | def test_note2midi_known_values_with_unicode_issues(self, note, midi): | ||
| 56 | " known values are correctly converted, unless decoding is expected to fail" | ||
| 57 | try: | ||
| 33 | self.assertEqual ( note2midi(note), midi ) | 58 | self.assertEqual ( note2midi(note), midi ) |
| 59 | except UnicodeEncodeError as e: | ||
| 60 | import sys | ||
| 61 | strfmt = "len(u'\\U0001D12A') != 1, excpected decoding failure | {:s} | {:s} {:s}" | ||
| 62 | strres = strfmt.format(e, sys.platform, sys.version) | ||
| 63 | # happens with: darwin 2.7.10, windows 2.7.12 | ||
| 64 | if len('\U0001D12A') != 1 and sys.version[0] == '2': | ||
| 65 | self.skipTest(strres + " | upgrade to Python 3 to fix") | ||
| 66 | else: | ||
| 67 | raise | ||
| 34 | 68 | ||
| 35 | class note2midi_wrong_values(unittest.TestCase): | 69 | class note2midi_wrong_values(unittest.TestCase): |
| 36 | 70 | ||
| @@ -66,12 +100,38 @@ class note2midi_wrong_values(unittest.TestCase): · 29 unmodified lines | |||
| 66 | " fails when passed a non-string value " | 100 | " fails when passed a non-string value " |
| 67 | self.assertRaises(TypeError, note2midi, 123) | 101 | self.assertRaises(TypeError, note2midi, 123) |
| 68 | 102 | ||
| 103 | def test_note2midi_wrong_data_too_long(self): | ||
| 104 | " fails when passed a note with a note name longer than expected" | ||
| 105 | self.assertRaises(ValueError, note2midi, 'CB+-3') | ||
| 106 | |||
| 107 | @params(*list_of_unknown_notes) | ||
| 108 | def test_note2midi_unknown_values(self, note): | ||
| 109 | " unknown values throw out an error " | ||
| 110 | self.assertRaises(ValueError, note2midi, note) | ||
| 69 | 111 | ||
| 70 | class freq2note_simple_test(unittest.TestCase): | 112 | class freq2note_simple_test(unittest.TestCase): |
| 71 | 113 | ||
| 72 | def test_freq2note(self): | 114 | def test_freq2note_above(self): |
| 73 | " make sure freq2note(441) == A4 " | 115 | " make sure freq2note(441) == A4 " |
| 74 | self.assertEqual("A4", freq2note(441)) | 116 | self.assertEqual("A4", freq2note(441)) |
| 75 | 117 | ||
| 118 | def test_freq2note_under(self): | ||
| 119 | " make sure freq2note(439) == A4 " | ||
| 120 | self.assertEqual("A4", freq2note(439)) | ||
| 121 | |||
| 122 | class note2freq_simple_test(unittest.TestCase): | ||
| 123 | |||
| 124 | def test_note2freq(self): | ||
| 125 | " make sure note2freq('A3') == 220" | ||
| 126 | self.assertEqual(220, note2freq("A3")) | ||
| 127 | |||
| 128 | def test_note2freq_under(self): | ||
| 129 | " make sure note2freq(A4) == 440" | ||
| 130 | if float_type == 'float32': | ||
| 131 | self.assertEqual(440, note2freq("A4")) | ||
| 132 | else: | ||
| 133 | self.assertLess(abs(note2freq("A4")-440), 1.e-12) | ||
| 134 | |||
| 76 | if __name__ == '__main__': | 135 | if __name__ == '__main__': |
| 77 | unittest.main() | 136 | import nose2 |
| 137 | nose2.main() | ||
| @@ -0,0 +1,95 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | from unittest import main | ||
| 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal | ||
| 5 | from aubio import notes | ||
| 6 | |||
| 7 | AUBIO_DEFAULT_NOTES_SILENCE = -70. | ||
| 8 | AUBIO_DEFAULT_NOTES_RELEASE_DROP = 10. | ||
| 9 | AUBIO_DEFAULT_NOTES_MINIOI_MS = 30. | ||
| 10 | |||
| 11 | class aubio_notes_default(TestCase): | ||
| 12 | |||
| 13 | def test_members(self): | ||
| 14 | o = notes() | ||
| 15 | assert_equal ([o.buf_size, o.hop_size, o.method, o.samplerate], | ||
| 16 | [1024,512,'default',44100]) | ||
| 17 | |||
| 18 | |||
| 19 | class aubio_notes_params(TestCase): | ||
| 20 | |||
| 21 | samplerate = 44100 | ||
| 22 | |||
| 23 | def setUp(self): | ||
| 24 | self.o = notes(samplerate = self.samplerate) | ||
| 25 | |||
| 26 | def test_get_minioi_ms(self): | ||
| 27 | assert_equal (self.o.get_minioi_ms(), AUBIO_DEFAULT_NOTES_MINIOI_MS) | ||
| 28 | |||
| 29 | def test_set_minioi_ms(self): | ||
| 30 | val = 40. | ||
| 31 | self.o.set_minioi_ms(val) | ||
| 32 | assert_almost_equal (self.o.get_minioi_ms(), val) | ||
| 33 | |||
| 34 | def test_get_silence(self): | ||
| 35 | assert_equal (self.o.get_silence(), AUBIO_DEFAULT_NOTES_SILENCE) | ||
| 36 | |||
| 37 | def test_set_silence(self): | ||
| 38 | val = -50 | ||
| 39 | self.o.set_silence(val) | ||
| 40 | assert_equal (self.o.get_silence(), val) | ||
| 41 | |||
| 42 | def test_get_release_drop(self): | ||
| 43 | assert_equal (self.o.get_release_drop(), AUBIO_DEFAULT_NOTES_RELEASE_DROP) | ||
| 44 | |||
| 45 | def test_set_release_drop(self): | ||
| 46 | val = 50 | ||
| 47 | self.o.set_release_drop(val) | ||
| 48 | assert_equal (self.o.get_release_drop(), val) | ||
| 49 | |||
| 50 | def test_set_release_drop_wrong(self): | ||
| 51 | val = -10 | ||
| 52 | with self.assertRaises(ValueError): | ||
| 53 | self.o.set_release_drop(val) | ||
| 54 | |||
| 55 | from .utils import list_all_sounds | ||
| 56 | list_of_sounds = list_all_sounds('sounds') | ||
| 57 | |||
| 58 | class aubio_notes_sinewave(TestCase): | ||
| 59 | |||
| 60 | def analyze_file(self, filepath, samplerate=0): | ||
| 61 | from aubio import source | ||
| 62 | import numpy as np | ||
| 63 | win_s = 512 # fft size | ||
| 64 | hop_s = 256 # hop size | ||
| 65 | |||
| 66 | s = source(filepath, samplerate, hop_s) | ||
| 67 | samplerate = s.samplerate | ||
| 68 | |||
| 69 | tolerance = 0.8 | ||
| 70 | |||
| 71 | notes_o = notes("default", win_s, hop_s, samplerate) | ||
| 72 | total_frames = 0 | ||
| 73 | |||
| 74 | results = [] | ||
| 75 | while True: | ||
| 76 | samples, read = s() | ||
| 77 | new_note = notes_o(samples) | ||
| 78 | if (new_note[0] != 0): | ||
| 79 | note_str = ' '.join(["%.2f" % i for i in new_note]) | ||
| 80 | results.append( [total_frames, np.copy(new_note)] ) | ||
| 81 | total_frames += read | ||
| 82 | if read < hop_s: break | ||
| 83 | return results | ||
| 84 | |||
| 85 | def test_sinewave(self): | ||
| 86 | for filepath in list_of_sounds: | ||
| 87 | if '44100Hz_44100f_sine441.wav' in filepath: | ||
| 88 | results = self.analyze_file(filepath) | ||
| 89 | assert_equal (len(results), 1) | ||
| 90 | assert_equal (len(results[0]), 2) | ||
| 91 | assert_equal (results[0][0], 1280) | ||
| 92 | assert_equal (results[0][1], [69, 123, -1]) | ||
| 93 | |||
| 94 | if __name__ == '__main__': | ||
| 95 | main() | ||
| @@ -2,7 +2,7 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | from unittest import main | 3 | from unittest import main |
| 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal | 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal |
| 5 | from aubio import onset | 5 | from aubio import onset, fvec |
| 6 | 6 | ||
| 7 | class aubio_onset_default(TestCase): | 7 | class aubio_onset_default(TestCase): |
| 8 | 8 | ||
| @@ -19,25 +19,25 @@ class aubio_onset_params(TestCase): · 10 unmodified lines | |||
| 19 | self.o = onset(samplerate = self.samplerate) | 19 | self.o = onset(samplerate = self.samplerate) |
| 20 | 20 | ||
| 21 | def test_get_delay(self): | 21 | def test_get_delay(self): |
| 22 | assert_equal (self.o.get_delay(), int(4.3 * self.o.hop_size)) | 22 | self.assertGreater(self.o.get_delay(), 0) |
| 23 | 23 | ||
| 24 | def test_get_delay_s(self): | 24 | def test_get_delay_s(self): |
| 25 | assert_almost_equal (self.o.get_delay_s(), self.o.get_delay() / float(self.samplerate)) | 25 | self.assertGreater(self.o.get_delay_s(), 0.) |
| 26 | 26 | ||
| 27 | def test_get_delay_ms(self): | 27 | def test_get_delay_ms(self): |
| 28 | assert_almost_equal (self.o.get_delay_ms(), self.o.get_delay() * 1000. / self.samplerate, 5) | 28 | self.assertGreater(self.o.get_delay_ms(), 0.) |
| 29 | 29 | ||
| 30 | def test_get_minioi(self): | 30 | def test_get_minioi(self): |
| 31 | assert_almost_equal (self.o.get_minioi(), 0.02 * self.samplerate) | 31 | self.assertGreater(self.o.get_minioi(), 0) |
| 32 | 32 | ||
| 33 | def test_get_minioi_s(self): | 33 | def test_get_minioi_s(self): |
| 34 | assert_almost_equal (self.o.get_minioi_s(), 0.02) | 34 | self.assertGreater(self.o.get_minioi_s(), 0.) |
| 35 | 35 | ||
| 36 | def test_get_minioi_ms(self): | 36 | def test_get_minioi_ms(self): |
| 37 | assert_equal (self.o.get_minioi_ms(), 20.) | 37 | self.assertGreater(self.o.get_minioi_ms(), 0.) |
| 38 | 38 | ||
| 39 | def test_get_threshold(self): | 39 | def test_get_threshold(self): |
| 40 | assert_almost_equal (self.o.get_threshold(), 0.3) | 40 | self.assertGreater(self.o.get_threshold(), 0.) |
| 41 | 41 | ||
| 42 | def test_set_delay(self): | 42 | def test_set_delay(self): |
| 43 | val = 256 | 43 | val = 256 |
| @@ -83,5 +83,37 @@ class aubio_onset_32000(aubio_onset_params): · 39 unmodified lines | |||
| 83 | class aubio_onset_8000(aubio_onset_params): | 83 | class aubio_onset_8000(aubio_onset_params): |
| 84 | samplerate = 8000 | 84 | samplerate = 8000 |
| 85 | 85 | ||
| 86 | class aubio_onset_coverate(TestCase): | ||
| 87 | # extra tests to execute the C routines and improve coverage | ||
| 88 | |||
| 89 | def test_all_methods(self): | ||
| 90 | for method in ['default', 'energy', 'hfc', 'complexdomain', 'complex', | ||
| 91 | 'phase', 'wphase', 'mkl', 'kl', 'specflux', 'specdiff', | ||
| 92 | 'old_default']: | ||
| 93 | o = onset(method=method, buf_size=512, hop_size=256) | ||
| 94 | o(fvec(256)) | ||
| 95 | |||
| 96 | def test_get_methods(self): | ||
| 97 | o = onset(method='default', buf_size=512, hop_size=256) | ||
| 98 | |||
| 99 | assert o.get_silence() == -70 | ||
| 100 | o.set_silence(-20) | ||
| 101 | assert_almost_equal(o.get_silence(), -20) | ||
| 102 | |||
| 103 | assert o.get_compression() == 1 | ||
| 104 | o.set_compression(.99) | ||
| 105 | assert_almost_equal(o.get_compression(), .99) | ||
| 106 | |||
| 107 | assert o.get_awhitening() == 0 | ||
| 108 | o.set_awhitening(1) | ||
| 109 | assert o.get_awhitening() == 1 | ||
| 110 | |||
| 111 | o.get_last() | ||
| 112 | o.get_last_ms() | ||
| 113 | o.get_last_s() | ||
| 114 | o.get_descriptor() | ||
| 115 | o.get_thresholded_descriptor() | ||
| 116 | |||
| 117 | |||
| 86 | if __name__ == '__main__': | 118 | if __name__ == '__main__': |
| 87 | main() | 119 | main() |
| @@ -46,9 +46,25 @@ class aubio_pvoc_test_case(TestCase): · 45 unmodified lines | |||
| 46 | r = f.rdo(s) | 46 | r = f.rdo(s) |
| 47 | assert_equal ( t, 0.) | 47 | assert_equal ( t, 0.) |
| 48 | assert_equal ( s.norm, 0.) | 48 | assert_equal ( s.norm, 0.) |
| 49 | assert_equal ( s.phas, 0.) | 49 | try: |
| 50 | assert_equal ( s.phas, 0 ) | ||
| 51 | except AssertionError: | ||
| 52 | assert_equal (s.phas[s.phas > 0], +np.pi) | ||
| 53 | assert_equal (s.phas[s.phas < 0], -np.pi) | ||
| 54 | assert_equal (np.abs(s.phas[np.abs(s.phas) != np.pi]), 0) | ||
| 55 | self.skipTest('pvoc(fvec(%d)).phas != +0, ' % win_s \ | ||
| 56 | + 'This is expected when using fftw3 on powerpc.') | ||
| 50 | assert_equal ( r, 0.) | 57 | assert_equal ( r, 0.) |
| 51 | 58 | ||
| 59 | def test_no_overlap(self): | ||
| 60 | win_s, hop_s = 1024, 1024 | ||
| 61 | f = pvoc (win_s, hop_s) | ||
| 62 | t = fvec (hop_s) | ||
| 63 | for _ in range(4): | ||
| 64 | s = f(t) | ||
| 65 | r = f.rdo(s) | ||
| 66 | assert_equal ( t, 0.) | ||
| 67 | |||
| 52 | @params( | 68 | @params( |
| 53 | ( 256, 8), | 69 | ( 256, 8), |
| 54 | ( 256, 4), | 70 | ( 256, 4), |
| @@ -70,8 +70,8 @@ class aubio_pitch_Sinusoid(TestCase): · 69 unmodified lines | |||
| 70 | #print 'len(pitches), cut:', len(pitches), cut | 70 | #print 'len(pitches), cut:', len(pitches), cut |
| 71 | #print 'median errors: ', median(errors), 'median pitches: ', median(pitches) | 71 | #print 'median errors: ', median(errors), 'median pitches: ', median(pitches) |
| 72 | 72 | ||
| 73 | pitch_algorithms = [ "default", "yinfft", "yin", "schmitt", "mcomb", "fcomb" , "specacf" ] | 73 | pitch_algorithms = [ "default", "yinfft", "yin", "yinfast", "schmitt", "mcomb", "fcomb" , "specacf" ] |
| 74 | pitch_algorithms = [ "default", "yinfft", "yin", "schmitt", "mcomb", "fcomb" ] | 74 | pitch_algorithms = [ "default", "yinfft", "yin", "yinfast", "schmitt", "mcomb", "fcomb" ] |
| 75 | 75 | ||
| 76 | #freqs = [ 27.5, 55., 110., 220., 440., 880., 1760., 3520. ] | 76 | #freqs = [ 27.5, 55., 110., 220., 440., 880., 1760., 3520. ] |
| 77 | freqs = [ 110., 220., 440., 880., 1760., 3520. ] | 77 | freqs = [ 110., 220., 440., 880., 1760., 3520. ] |
| @@ -4,7 +4,10 @@ from nose2 import main · 3 unmodified lines | |||
| 4 | from nose2.tools import params | 4 | from nose2.tools import params |
| 5 | from numpy.testing import TestCase | 5 | from numpy.testing import TestCase |
| 6 | from aubio import fvec, source, sink | 6 | from aubio import fvec, source, sink |
| 7 | from utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path | 7 | from .utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path |
| 8 | |||
| 9 | import warnings | ||
| 10 | warnings.filterwarnings('ignore', category=UserWarning, append=True) | ||
| 8 | 11 | ||
| 9 | list_of_sounds = list_all_sounds('sounds') | 12 | list_of_sounds = list_all_sounds('sounds') |
| 10 | samplerates = [0, 44100, 8000, 32000] | 13 | samplerates = [0, 44100, 8000, 32000] |
| @@ -26,6 +29,26 @@ class aubio_sink_test_case(TestCase): · 15 unmodified lines | |||
| 26 | if not len(list_of_sounds): | 29 | if not len(list_of_sounds): |
| 27 | self.skipTest('add some sound files in \'python/tests/sounds\'') | 30 | self.skipTest('add some sound files in \'python/tests/sounds\'') |
| 28 | 31 | ||
| 32 | def test_wrong_filename(self): | ||
| 33 | with self.assertRaises(RuntimeError): | ||
| 34 | sink('') | ||
| 35 | |||
| 36 | def test_wrong_samplerate(self): | ||
| 37 | with self.assertRaises(RuntimeError): | ||
| 38 | sink(get_tmp_sink_path(), -1) | ||
| 39 | |||
| 40 | def test_wrong_samplerate_too_large(self): | ||
| 41 | with self.assertRaises(RuntimeError): | ||
| 42 | sink(get_tmp_sink_path(), 1536001, 2) | ||
| 43 | |||
| 44 | def test_wrong_channels(self): | ||
| 45 | with self.assertRaises(RuntimeError): | ||
| 46 | sink(get_tmp_sink_path(), 44100, -1) | ||
| 47 | |||
| 48 | def test_wrong_channels_too_large(self): | ||
| 49 | with self.assertRaises(RuntimeError): | ||
| 50 | sink(get_tmp_sink_path(), 44100, 202020) | ||
| 51 | |||
| 29 | def test_many_sinks(self): | 52 | def test_many_sinks(self): |
| 30 | from tempfile import mkdtemp | 53 | from tempfile import mkdtemp |
| 31 | import os.path | 54 | import os.path |
| @@ -93,5 +116,13 @@ class aubio_sink_test_case(TestCase): · 61 unmodified lines | |||
| 93 | g.close() | 116 | g.close() |
| 94 | del_tmp_sink_path(sink_path) | 117 | del_tmp_sink_path(sink_path) |
| 95 | 118 | ||
| 119 | def test_read_with(self): | ||
| 120 | samplerate = 44100 | ||
| 121 | sink_path = get_tmp_sink_path() | ||
| 122 | vec = fvec(128) | ||
| 123 | with sink(sink_path, samplerate) as g: | ||
| 124 | for _ in range(10): | ||
| 125 | g(vec, 128) | ||
| 126 | |||
| 96 | if __name__ == '__main__': | 127 | if __name__ == '__main__': |
| 97 | main() | 128 | main() |
| @@ -3,8 +3,8 @@ · 2 unmodified lines | |||
| 3 | from unittest import main | 3 | from unittest import main |
| 4 | from numpy.testing import TestCase, assert_equal | 4 | from numpy.testing import TestCase, assert_equal |
| 5 | from aubio import slice_source_at_stamps | 5 | from aubio import slice_source_at_stamps |
| 6 | from utils import count_files_in_directory, get_default_test_sound | 6 | from .utils import count_files_in_directory, get_default_test_sound |
| 7 | from utils import count_samples_in_directory, count_samples_in_file | 7 | from .utils import count_samples_in_directory, count_samples_in_file |
| 8 | 8 | ||
| 9 | import tempfile | 9 | import tempfile |
| 10 | import shutil | 10 | import shutil |
| @@ -23,19 +23,27 @@ class aubio_slicing_test_case(TestCase): · 12 unmodified lines | |||
| 23 | 23 | ||
| 24 | def test_slice_start_only_no_zero(self): | 24 | def test_slice_start_only_no_zero(self): |
| 25 | regions_start = [i*1000 for i in range(1, n_slices)] | 25 | regions_start = [i*1000 for i in range(1, n_slices)] |
| 26 | slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir) | 26 | slice_source_at_stamps(self.source_file, regions_start, |
| 27 | output_dir = self.output_dir, create_first=True) | ||
| 27 | 28 | ||
| 28 | def test_slice_start_beyond_end(self): | 29 | def test_slice_start_beyond_end(self): |
| 29 | regions_start = [i*1000 for i in range(1, n_slices)] | 30 | regions_start = [i*1000 for i in range(1, n_slices)] |
| 30 | regions_start += [count_samples_in_file(self.source_file) + 1000] | 31 | regions_start += [count_samples_in_file(self.source_file) + 1000] |
| 31 | slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir) | 32 | slice_source_at_stamps(self.source_file, regions_start, |
| 33 | output_dir = self.output_dir, create_first=True) | ||
| 32 | 34 | ||
| 33 | def test_slice_start_every_blocksize(self): | 35 | def test_slice_start_every_blocksize(self): |
| 34 | hopsize = 200 | 36 | hopsize = 200 |
| 35 | regions_start = [i*hopsize for i in range(1, n_slices)] | 37 | regions_start = [i*hopsize for i in range(0, n_slices)] |
| 36 | slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir, | 38 | slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir, |
| 37 | hopsize = 200) | 39 | hopsize = 200) |
| 38 | 40 | ||
| 41 | def test_slice_start_every_half_blocksize(self): | ||
| 42 | hopsize = 200 | ||
| 43 | regions_start = [i*hopsize//2 for i in range(0, n_slices)] | ||
| 44 | slice_source_at_stamps(self.source_file, regions_start, | ||
| 45 | output_dir = self.output_dir, hopsize = 200) | ||
| 46 | |||
| 39 | def tearDown(self): | 47 | def tearDown(self): |
| 40 | original_samples = count_samples_in_file(self.source_file) | 48 | original_samples = count_samples_in_file(self.source_file) |
| 41 | written_samples = count_samples_in_directory(self.output_dir) | 49 | written_samples = count_samples_in_directory(self.output_dir) |
| @@ -91,6 +99,19 @@ class aubio_slicing_with_ends_test_case(TestCase): · 49 unmodified lines | |||
| 91 | assert_equal(written_samples, expected_samples, | 99 | assert_equal(written_samples, expected_samples, |
| 92 | "number of samples written different from number of original samples") | 100 | "number of samples written different from number of original samples") |
| 93 | 101 | ||
| 102 | def test_slice_start_and_ends_with_missing_end(self): | ||
| 103 | regions_start = [i*1000 for i in range(n_slices)] | ||
| 104 | regions_ends = [r-1 for r in regions_start[1:]] | ||
| 105 | slice_source_at_stamps(self.source_file, regions_start, regions_ends, | ||
| 106 | output_dir = self.output_dir) | ||
| 107 | written_samples = count_samples_in_directory(self.output_dir) | ||
| 108 | original_samples = count_samples_in_file(self.source_file) | ||
| 109 | total_files = count_files_in_directory(self.output_dir) | ||
| 110 | assert_equal(n_slices, total_files, | ||
| 111 | "number of slices created different from expected") | ||
| 112 | assert_equal(written_samples, original_samples, | ||
| 113 | "number of samples written different from number of original samples") | ||
| 114 | |||
| 94 | def tearDown(self): | 115 | def tearDown(self): |
| 95 | shutil.rmtree(self.output_dir) | 116 | shutil.rmtree(self.output_dir) |
| 96 | 117 | ||
| @@ -133,7 +154,7 @@ class aubio_slicing_wrong_ends_test_case(TestCase): · 36 unmodified lines | |||
| 133 | regions_start = [i*1000 for i in range(1, n_slices)] | 154 | regions_start = [i*1000 for i in range(1, n_slices)] |
| 134 | regions_end = None | 155 | regions_end = None |
| 135 | slice_source_at_stamps (self.source_file, regions_start, regions_end, | 156 | slice_source_at_stamps (self.source_file, regions_start, regions_end, |
| 136 | output_dir = self.output_dir) | 157 | output_dir = self.output_dir, create_first=True) |
| 137 | total_files = count_files_in_directory(self.output_dir) | 158 | total_files = count_files_in_directory(self.output_dir) |
| 138 | assert_equal(n_slices, total_files, | 159 | assert_equal(n_slices, total_files, |
| 139 | "number of slices created different from expected") | 160 | "number of slices created different from expected") |
| @@ -2,9 +2,9 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | from nose2 import main | 3 | from nose2 import main |
| 4 | from nose2.tools import params | 4 | from nose2.tools import params |
| 5 | from numpy.testing import TestCase | 5 | from numpy.testing import TestCase, assert_equal |
| 6 | from aubio import source | 6 | from aubio import source |
| 7 | from utils import list_all_sounds | 7 | from .utils import list_all_sounds |
| 8 | 8 | ||
| 9 | import warnings | 9 | import warnings |
| 10 | warnings.filterwarnings('ignore', category=UserWarning, append=True) | 10 | warnings.filterwarnings('ignore', category=UserWarning, append=True) |
| @@ -51,9 +51,11 @@ class aubio_source_read_test_case(aubio_source_test_case_base): · 40 unmodified lines | |||
| 51 | def read_from_source(self, f): | 51 | def read_from_source(self, f): |
| 52 | total_frames = 0 | 52 | total_frames = 0 |
| 53 | while True: | 53 | while True: |
| 54 | _ , read = f() | 54 | samples , read = f() |
| 55 | total_frames += read | 55 | total_frames += read |
| 56 | if read < f.hop_size: break | 56 | if read < f.hop_size: |
| 57 | assert_equal(samples[read:], 0) | ||
| 58 | break | ||
| 57 | #result_str = "read {:.2f}s ({:d} frames in {:d} blocks at {:d}Hz) from {:s}" | 59 | #result_str = "read {:.2f}s ({:d} frames in {:d} blocks at {:d}Hz) from {:s}" |
| 58 | #result_params = total_frames / float(f.samplerate), total_frames, total_frames//f.hop_size, f.samplerate, f.uri | 60 | #result_params = total_frames / float(f.samplerate), total_frames, total_frames//f.hop_size, f.samplerate, f.uri |
| 59 | #print (result_str.format(*result_params)) | 61 | #print (result_str.format(*result_params)) |
| @@ -66,7 +68,14 @@ class aubio_source_read_test_case(aubio_source_test_case_base): · 6 unmodified lines | |||
| 66 | except RuntimeError as e: | 68 | except RuntimeError as e: |
| 67 | self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e))) | 69 | self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e))) |
| 68 | assert f.samplerate != 0 | 70 | assert f.samplerate != 0 |
| 69 | self.read_from_source(f) | 71 | read_frames = self.read_from_source(f) |
| 72 | if 'f_' in soundfile and samplerate == 0: | ||
| 73 | import re | ||
| 74 | f = re.compile('.*_\([0:9]*f\)_.*') | ||
| 75 | match_f = re.findall('([0-9]*)f_', soundfile) | ||
| 76 | if len(match_f) == 1: | ||
| 77 | expected_frames = int(match_f[0]) | ||
| 78 | self.assertEqual(expected_frames, read_frames) | ||
| 70 | 79 | ||
| 71 | @params(*list_of_sounds) | 80 | @params(*list_of_sounds) |
| 72 | def test_samplerate_none(self, p): | 81 | def test_samplerate_none(self, p): |
| @@ -149,13 +158,31 @@ class aubio_source_readmulti_test_case(aubio_source_read_test_case): · 76 unmodified lines | |||
| 149 | def read_from_source(self, f): | 158 | def read_from_source(self, f): |
| 150 | total_frames = 0 | 159 | total_frames = 0 |
| 151 | while True: | 160 | while True: |
| 152 | _, read = f.do_multi() | 161 | samples, read = f.do_multi() |
| 153 | total_frames += read | 162 | total_frames += read |
| 154 | if read < f.hop_size: break | 163 | if read < f.hop_size: |
| 164 | assert_equal(samples[:,read:], 0) | ||
| 165 | break | ||
| 155 | #result_str = "read {:.2f}s ({:d} frames in {:d} channels and {:d} blocks at {:d}Hz) from {:s}" | 166 | #result_str = "read {:.2f}s ({:d} frames in {:d} channels and {:d} blocks at {:d}Hz) from {:s}" |
| 156 | #result_params = total_frames / float(f.samplerate), total_frames, f.channels, int(total_frames/f.hop_size), f.samplerate, f.uri | 167 | #result_params = total_frames / float(f.samplerate), total_frames, f.channels, int(total_frames/f.hop_size), f.samplerate, f.uri |
| 157 | #print (result_str.format(*result_params)) | 168 | #print (result_str.format(*result_params)) |
| 158 | return total_frames | 169 | return total_frames |
| 159 | 170 | ||
| 171 | class aubio_source_with(aubio_source_test_case_base): | ||
| 172 | |||
| 173 | #@params(*list_of_sounds) | ||
| 174 | @params(*list_of_sounds) | ||
| 175 | def test_read_from_mono(self, filename): | ||
| 176 | total_frames = 0 | ||
| 177 | hop_size = 2048 | ||
| 178 | with source(filename, 0, hop_size) as input_source: | ||
| 179 | assert_equal(input_source.hop_size, hop_size) | ||
| 180 | #assert_equal(input_source.samplerate, samplerate) | ||
| 181 | total_frames = 0 | ||
| 182 | for frames in input_source: | ||
| 183 | total_frames += frames.shape[-1] | ||
| 184 | # check we read as many samples as we expected | ||
| 185 | assert_equal(total_frames, input_source.duration) | ||
| 186 | |||
| 160 | if __name__ == '__main__': | 187 | if __name__ == '__main__': |
| 161 | main() | 188 | main() |
| @@ -0,0 +1,93 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """A brute force test using `sink` to create and write samples to a stereo | ||
| 4 | file, then `source` to check the correct content is read from the files.""" | ||
| 5 | |||
| 6 | import os.path | ||
| 7 | import unittest | ||
| 8 | import aubio | ||
| 9 | import numpy as np | ||
| 10 | from numpy.testing import assert_equal | ||
| 11 | from .utils import get_tmp_sink_path | ||
| 12 | |||
| 13 | class aubio_source_test_case(unittest.TestCase): | ||
| 14 | |||
| 15 | def test_read_from_mono(self): | ||
| 16 | out = get_tmp_sink_path() | ||
| 17 | samplerate = 44100 | ||
| 18 | hop_size = 256 | ||
| 19 | blocks = 10 | ||
| 20 | channels = 1 | ||
| 21 | write_samples = np.ones([channels, hop_size], dtype=aubio.float_type) | ||
| 22 | write_samples *= .5 | ||
| 23 | self.check_write_and_read(samplerate, channels, hop_size, blocks, | ||
| 24 | write_samples) | ||
| 25 | |||
| 26 | def test_read_from_stereo(self): | ||
| 27 | out = get_tmp_sink_path() | ||
| 28 | samplerate = 44100 | ||
| 29 | hop_size = 256 | ||
| 30 | blocks = 10 | ||
| 31 | channels = 1 | ||
| 32 | write_samples = np.ones([channels, hop_size], dtype=aubio.float_type) | ||
| 33 | write_samples *= .5 | ||
| 34 | self.check_write_and_read(samplerate, channels, hop_size, blocks, | ||
| 35 | write_samples) | ||
| 36 | |||
| 37 | def test_read_from_half_stereo(self): | ||
| 38 | samplerate = 16000 | ||
| 39 | channels = 2 | ||
| 40 | hop_size = 512 | ||
| 41 | blocks = 10 | ||
| 42 | write_samples = np.ones([channels, hop_size], dtype=aubio.float_type) | ||
| 43 | write_samples *= .5 | ||
| 44 | write_samples[1, :] = 0 | ||
| 45 | self.check_write_and_read(samplerate, channels, hop_size, blocks, | ||
| 46 | write_samples) | ||
| 47 | |||
| 48 | def test_read_from_cancelling_channels(self): | ||
| 49 | samplerate = 16000 | ||
| 50 | channels = 2 | ||
| 51 | hop_size = 512 | ||
| 52 | blocks = 10 | ||
| 53 | write_samples = np.ones([channels, hop_size], dtype=aubio.float_type) | ||
| 54 | write_samples *= .5 | ||
| 55 | write_samples[1] *= -1 | ||
| 56 | self.check_write_and_read(samplerate, channels, hop_size, blocks, | ||
| 57 | write_samples) | ||
| 58 | |||
| 59 | def test_read_from_strange_three_channels(self): | ||
| 60 | samplerate = 8000 | ||
| 61 | channels = 3 | ||
| 62 | hop_size = 123 | ||
| 63 | blocks = 10 | ||
| 64 | write_samples = np.ones([channels, hop_size], dtype=aubio.float_type) | ||
| 65 | write_samples *= .5 | ||
| 66 | write_samples[1, :] = 0 | ||
| 67 | self.check_write_and_read(samplerate, channels, hop_size, blocks, | ||
| 68 | write_samples) | ||
| 69 | |||
| 70 | def check_write_and_read(self, samplerate, channels, | ||
| 71 | hop_size, blocks, write_samples): | ||
| 72 | expected_mono = np.sum(write_samples, axis=0)/write_samples.shape[0] | ||
| 73 | out = get_tmp_sink_path() | ||
| 74 | snk = aubio.sink(out, samplerate, channels=channels) | ||
| 75 | for i in range(blocks): | ||
| 76 | snk.do_multi(write_samples, hop_size) | ||
| 77 | # close the sink before reading from it | ||
| 78 | snk.close() | ||
| 79 | |||
| 80 | src = aubio.source(out, samplerate, hop_size) | ||
| 81 | for i in range(blocks): | ||
| 82 | read_samples, read = src.do_multi() | ||
| 83 | assert_equal (read_samples, write_samples) | ||
| 84 | assert_equal (read, hop_size) | ||
| 85 | |||
| 86 | src.seek(0) | ||
| 87 | for i in range(blocks): | ||
| 88 | read_samples, read = src() | ||
| 89 | assert_equal (read, hop_size) | ||
| 90 | assert_equal (read_samples, expected_mono) | ||
| 91 | |||
| 92 | if __name__ == '__main__': | ||
| 93 | unittest.main() | ||
| @@ -0,0 +1,91 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | from unittest import main | ||
| 4 | from numpy.testing import TestCase, assert_equal, assert_almost_equal | ||
| 5 | import aubio | ||
| 6 | |||
| 7 | class aubio_tempo_default(TestCase): | ||
| 8 | |||
| 9 | def test_members(self): | ||
| 10 | o = aubio.tempo() | ||
| 11 | assert_equal ([o.buf_size, o.hop_size, o.method, o.samplerate], | ||
| 12 | [1024,512,'default',44100]) | ||
| 13 | |||
| 14 | class aubio_tempo_params(TestCase): | ||
| 15 | |||
| 16 | samplerate = 44100 | ||
| 17 | |||
| 18 | def setUp(self): | ||
| 19 | self.o = aubio.tempo(samplerate = self.samplerate) | ||
| 20 | |||
| 21 | def test_get_delay(self): | ||
| 22 | self.assertEqual(self.o.get_delay(), 0) | ||
| 23 | |||
| 24 | def test_set_delay(self): | ||
| 25 | val = 256 | ||
| 26 | self.o.set_delay(val) | ||
| 27 | assert_equal (self.o.get_delay(), val) | ||
| 28 | |||
| 29 | def test_get_delay_s(self): | ||
| 30 | self.assertEqual(self.o.get_delay_s(), 0.) | ||
| 31 | |||
| 32 | def test_set_delay_s(self): | ||
| 33 | val = .05 | ||
| 34 | self.o.set_delay_s(val) | ||
| 35 | assert_almost_equal (self.o.get_delay_s(), val) | ||
| 36 | |||
| 37 | def test_get_delay_ms(self): | ||
| 38 | self.assertEqual(self.o.get_delay_ms(), 0.) | ||
| 39 | |||
| 40 | def test_set_delay_ms(self): | ||
| 41 | val = 50. | ||
| 42 | self.o.set_delay_ms(val) | ||
| 43 | assert_almost_equal (self.o.get_delay_ms(), val) | ||
| 44 | |||
| 45 | def test_get_threshold(self): | ||
| 46 | assert_almost_equal(self.o.get_threshold(), 0.3) | ||
| 47 | |||
| 48 | def test_set_threshold(self): | ||
| 49 | val = .1 | ||
| 50 | self.o.set_threshold(val) | ||
| 51 | assert_almost_equal (self.o.get_threshold(), val) | ||
| 52 | |||
| 53 | def test_get_silence(self): | ||
| 54 | self.assertEqual(self.o.get_silence(), -90.) | ||
| 55 | |||
| 56 | def test_set_silence(self): | ||
| 57 | val = -50. | ||
| 58 | self.o.set_silence(val) | ||
| 59 | assert_almost_equal (self.o.get_silence(), val) | ||
| 60 | |||
| 61 | def test_get_last(self): | ||
| 62 | self.assertEqual(self.o.get_last(), 0.) | ||
| 63 | |||
| 64 | def test_get_last_s(self): | ||
| 65 | self.assertEqual(self.o.get_last_s(), 0.) | ||
| 66 | |||
| 67 | def test_get_last_ms(self): | ||
| 68 | self.assertEqual(self.o.get_last_ms(), 0.) | ||
| 69 | |||
| 70 | def test_get_period(self): | ||
| 71 | self.assertEqual(self.o.get_period(), 0.) | ||
| 72 | |||
| 73 | def test_get_period_s(self): | ||
| 74 | self.assertEqual(self.o.get_period_s(), 0.) | ||
| 75 | |||
| 76 | def test_get_last_tatum(self): | ||
| 77 | self.assertEqual(self.o.get_last_tatum(), 0.) | ||
| 78 | |||
| 79 | def test_set_tatum_signature(self): | ||
| 80 | self.o.set_tatum_signature(8) | ||
| 81 | self.o.set_tatum_signature(64) | ||
| 82 | self.o.set_tatum_signature(1) | ||
| 83 | |||
| 84 | def test_set_wrong_tatum_signature(self): | ||
| 85 | with self.assertRaises(ValueError): | ||
| 86 | self.o.set_tatum_signature(101) | ||
| 87 | with self.assertRaises(ValueError): | ||
| 88 | self.o.set_tatum_signature(0) | ||
| 89 | |||
| 90 | if __name__ == '__main__': | ||
| 91 | main() | ||