# pre-installed python version, see:
# http://www.appveyor.com/docs/installed-software#python
- - PYTHONDIR: "C:\\Python27"
- PYTHON_VERSION: "2.7.x"
- PYTHON_ARCH: "32"
+ - PYTHONDIR: C:\Python27
+ PYTHON_VERSION: 2.7.x
+ PYTHON_ARCH: 32
- - PYTHONDIR: "C:\\Python27-x64"
- PYTHON_VERSION: "2.7.x"
- PYTHON_ARCH: "64"
+ - PYTHONDIR: C:\Python27-x64
+ PYTHON_VERSION: 2.7.x
+ PYTHON_ARCH: 64
- - PYTHONDIR: "C:\\Python34"
- PYTHON_VERSION: "3.4.x"
- PYTHON_ARCH: "32"
+ - PYTHONDIR: C:\Python35
+ PYTHON_VERSION: 3.5.x
+ PYTHON_ARCH: 32
- - PYTHONDIR: "C:\\Python34-x64"
- PYTHON_VERSION: "3.4.x"
- PYTHON_ARCH: "64"
+ - PYTHONDIR: C:\Python35-x64
+ PYTHON_VERSION: 3.5.x
+ PYTHON_ARCH: 64
- - PYTHONDIR: "C:\\Python35"
- PYTHON_VERSION: "3.5.x"
- PYTHON_ARCH: "32"
+ - PYTHONDIR: C:\Python36
+ PYTHON_VERSION: 3.6.x
+ PYTHON_ARCH: 32
- - PYTHONDIR: "C:\\Python35-x64"
- PYTHON_VERSION: "3.5.x"
- PYTHON_ARCH: "64"
- # add path required to run preprocessor step
- PATH_EXTRAS: "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin"
+ - PYTHONDIR: C:\Python36-x64
+ PYTHON_VERSION: 3.6.x
+ PYTHON_ARCH: 64
-install:
+ - PYTHONDIR: C:\Python37
+ PYTHON_VERSION: 3.7.x
+ PYTHON_ARCH: 32
+
+ - PYTHONDIR: C:\Python37-x64
+ PYTHON_VERSION: 3.7.x
+ PYTHON_ARCH: 64
+install:
- ECHO "Installed SDKs:"
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
+ - "SET PATH=%PYTHONDIR%;%PYTHONDIR%\\Scripts;%PATH%"
+
# Check that we have the expected version and architecture for Python
- - "%PYTHONDIR%\\python.exe --version"
- - "%PYTHONDIR%\\python.exe -c \"import struct; print(struct.calcsize('P') * 8)\""
+ - "python --version"
+ - "python -c \"import struct; print(struct.calcsize('P') * 8)\""
- # We need wheel installed to build wheels
- - "%PYTHONDIR%\\python.exe -m pip install wheel"
+ - "python -m pip install --disable-pip-version-check --user --upgrade pip"
+ - "python -m pip install --upgrade setuptools"
- - "SET PATH=%PATH_EXTRAS%;%PYTHONDIR%;%PYTHONDIR%\\Scripts;%PATH%"
+ # We need wheel installed to build wheels
+ - "python -m pip install wheel"
- - "pip install --disable-pip-version-check --user --upgrade pip"
- - "pip install --upgrade setuptools"
+ - "pip install -r requirements.txt"
before_build:
- "bash scripts/get_waf.sh"
build_script:
- # build python module without using libaubio
- - "pip install -r requirements.txt"
- - "python setup.py build"
- - "pip install ."
+ # also build libaubio with waf
+ - python waf configure build install --verbose --msvc_version="msvc 14.0"
+ # clean before building python package
+ - python waf distclean
+ # build, upload and install wheel (inspired by numpy's appveyor)
+ - ps: |
+ pip wheel -v -v -v --wheel-dir=dist .
+ ls dist -r | Foreach-Object {
+ Push-AppveyorArtifact $_.FullName
+ pip install $_.FullName
+ }
+
+test_script:
- "python python\\demos\\demo_create_test_sounds.py"
- - "nose2 --verbose"
- # clean up
- - "python waf distclean"
- # build libaubio
- - python waf configure build --verbose --msvc_version="msvc 14.0"
+ - "pytest --verbose"
--- /dev/null
+apt-run: &apt-install
+ name: Install apt packages
+ command: |
+ sudo apt-get update
+ sudo apt-get -y install make sox pkg-config libavcodec-dev libavformat-dev libavresample-dev libavutil-dev libsndfile1-dev libsamplerate-dev
+
+pip-install: &pip-install
+ name: Install pip dependencies
+ command: |
+ pip install --user -r requirements.txt
+
+build-wheel: &build-wheel
+ name: Build python wheel
+ command: |
+ pip wheel -v -v -v --wheel-dir=dist .
+
+install-wheel: &install-wheel
+ name: Install python wheel
+ command: |
+ pip install --user dist/aubio*.whl
+
+test-pytest: &test-pytest
+ name: Test python wheel
+ command: |
+ make create_test_sounds
+ PATH=/home/circleci/.local/bin:$PATH pytest -v
+
+test-pytest-nosounds: &test-pytest-nosounds
+ name: Test python wheel
+ command: |
+ PATH=/home/circleci/.local/bin:$PATH pytest -v
+
+uninstall-wheel: &uninstall-wheel
+ name: Uninstall python wheel
+ command: |
+ pip show -f aubio
+ pip uninstall --verbose --yes aubio
+
+version: 2
+jobs:
+ build-27:
+ docker:
+ - image: circleci/python:2.7
+ steps:
+ - checkout
+ - run: *apt-install
+ - run: *pip-install
+ - run: *build-wheel
+ - run: *install-wheel
+ - run: *test-pytest
+ - run: *uninstall-wheel
+ - store_artifacts:
+ path: dist/
+
+ build-36:
+ docker:
+ - image: circleci/python:3.6
+ steps:
+ - checkout
+ - run: *apt-install
+ - run: *pip-install
+ - run: *build-wheel
+ - run: *install-wheel
+ - run: *test-pytest
+ - run: *uninstall-wheel
+ - store_artifacts:
+ path: dist/
+
+ build-37:
+ docker:
+ - image: circleci/python:3.7
+ steps:
+ - checkout
+ - run: *apt-install
+ - run: *pip-install
+ - run: *build-wheel
+ - run: *install-wheel
+ - run: *test-pytest
+ - run: *uninstall-wheel
+ - store_artifacts:
+ path: dist/
+
+ build-37-nodeps:
+ docker:
+ - image: circleci/python:3.7
+ steps:
+ - checkout
+ - run: *pip-install
+ - run: *build-wheel
+ - run: *install-wheel
+ - run: *test-pytest-nosounds
+ - run: *uninstall-wheel
+ - store_artifacts:
+ path: dist/
+
+workflows:
+ version: 2
+
+ test-wheel:
+ jobs:
+ - build-27
+ - build-36
+ - build-37
+ - build-37-nodeps
# gcov generated files
*.gcno
*.gcda
+python/lib/aubio/_aubio.*.so
+.coverage
# ignore compiled examples
RE:examples/[a-z]*
aubio-*.tar.bz2
aubio-*.zip
dist/*.tar.gz
+dist/*.whl
# test sounds
python/tests/sounds
aubio.egg-info
+.eggs
+.cache
+++ /dev/null
-strictness: medium
-test-warnings: true
-python-targets:
- - 2
- - 3
matrix:
include:
- - python: 3.5
+ - python: 3.6
os: linux
compiler: gcc
- - python: 3.4
+ - python: 3.5
os: linux
compiler: gcc
+ env: WAFOPTS="--build-type=debug"
- python: 2.7
os: linux
compiler: gcc
- - language: C
- os: osx
- osx_image: xcode8
- compiler: clang
- - python: 3.5
+ - python: "pypy3.5"
os: linux
compiler: gcc
- env: CFLAGS="-Os" WAFOPTS="--disable-samplerate --disable-sndfile"
- - python: 3.4
+ env: CFLAGS="-Os" WAFOPTS="--disable-avcodec"
+ - python: 3.6
+ os: linux
+ compiler: gcc
+ env: CFLAGS="-Os" WAFOPTS="--disable-samplerate"
+ - python: 3.5
os: linux
compiler: gcc
env: HAVE_AUBIO_DOUBLE=1 CFLAGS="-O3" WAFOPTS="--enable-fftw3"
env: CFLAGS="`dpkg-buildflags --get CFLAGS`" LDFLAGS="`dpkg-buildflags --get LDFLAGS`"
- language: C
os: osx
- osx_image: xcode8
compiler: clang
- env: CFLAGS="-Os" HAVE_AUBIO_DOUBLE=1 WAFOPTS="--disable-accelerate"
- language: C
os: osx
- osx_image: xcode8
compiler: clang
- env: WAFOPTS="--enable-fat --disable-avcodec --disable-sndfile"
- - language: C
- os: osx
- osx_image: xcode8
- compiler: clang
- env: WAFOPTS="--with-target-platform=ios --disable-avcodec --disable-sndfile" AUBIO_NOTESTS=1
- - language: C
- os: osx
- osx_image: xcode8
- compiler: clang
- env: WAFOPTS="--with-target-platform=iosimulator --disable-avcodec --disable-sndfile" AUBIO_NOTESTS=1
+ env: CFLAGS="-Os" HAVE_AUBIO_DOUBLE=1 WAFOPTS="--disable-accelerate"
- language: C
os: osx
- osx_image: xcode8.2
compiler: clang
env: WAFOPTS="--enable-fat --disable-avcodec --disable-sndfile"
- language: C
os: osx
- osx_image: xcode8.2
compiler: clang
env: WAFOPTS="--with-target-platform=ios --disable-avcodec --disable-sndfile" AUBIO_NOTESTS=1
- language: C
os: osx
- osx_image: xcode8.2
compiler: clang
env: WAFOPTS="--with-target-platform=iosimulator --disable-avcodec --disable-sndfile" AUBIO_NOTESTS=1
- libfftw3-dev
- sox
- lcov
+ homebrew:
+ packages:
+ - sox
+ - ffmpeg
+ - libsndfile
+ - lcov
+ #update: true
before_install:
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
- brew update
- brew install sox
- brew install ffmpeg
- brew install libsndfile
- brew install lcov
export PATH="$HOME/Library/Python/2.7/bin/:$PATH"
fi;
install:
+ - |
+ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+ alias pip=pip2
+ fi;
- travis_retry pip install --upgrade pip
- travis_retry make getwaf expandwaf deps_python
- which pip
- pip --version
- - pip install python-coveralls
- - gem install coveralls-lcov
+ - pip install coverage
script:
- make create_test_sounds
after_success:
- |
if [[ -z "$AUBIO_NOTESTS" ]]; then
- # upload lcov coverage to coveralls.io
- coveralls-lcov build/coverage.info
- # upload python coverage
- #coveralls
# upload to codecov
bash <(curl -s https://codecov.io/bash)
fi
+2018-12-19 Paul Brossier <piem@aubio.org>
+
+ [ Overview ]
+
+ * VERSION: bump to 0.4.9
+ * library: improve stability, fixing potential crashes and memory leaks on
+ invalid arguments; improve library messages and reporting of system errors
+ * tests/: major clean-up, check return codes, increase code coverage
+ * python/tests/: switch to pytest (closes gh-163), check emitted warnings
+ * python/: add pages to manual with brief descriptions of classes
+
+ [ Fixes ]
+
+ * security: improve arguments validation in new_aubio_filterbank (prevent
+ possible null-pointer dereference on invalid n_filters, CVE-2018-19801),
+ new_aubio-tempo (prevent possible buffer overflow, CVE-2018-19800), and
+ new_aubio_onset (prevent null-pointer dereference, CVE-2018-19802). Thanks
+ to Guoxiang Niu (@niugx), from the EaglEye Team for reporting these issues.
+ * tempo: fix delay_ms methods
+ * filterbank: fix aubio_filterbank_get_power (thanks to @romanbsd who
+ also noticed this issue)
+ * dct: creation fail on negative sizes or invalid accelerate radix,
+ fix typo in error and warning messages, prevent possible memory leak
+ * pitch: prevent null pointer dereference in yinfast, comment out unused
+ functions in mcomb and yin, prevent possible leak in specacf
+ * mfcc: always use dct module, strengthen input validation, change
+ get_{scale,power} to return smpl_t
+ * specdesc: improve error message
+ * notes: prevent null pointer dereference
+ * hist: add validation for size argument, prevent possible leak
+ * awhitening: use shortest length available (closes gh-216)
+ * io: add macros to display system errors, add helpers to validate input
+ arguments of source and sink methods, always clean-up after failure
+ * source: validate input sizes to prevent invalid reads
+ * apple_audio: use native format conversions in source and sink, prevent
+ possible apple_audio crash on empty string, get_duration returns 0 on failure
+ * ffmpeg/avcodec: prevent deprecation warnings, read after close, and skipped
+ samples warnings, improve warning messages, only show a warning when
+ swr_convert failed, prevent possible memory leak when closing swr context
+ * wavwrite: copy to all channels if needed, check fseek and fwrite return
+ values, call fflush in open to return failure on full disk-system
+ * source_sndfile: fix reading sizes when resampling, set error message when
+ reading after close
+ * aubio_priv.h: include blas first (see gh-225), add STRERROR macros
+
+ [ Python ]
+
+ * documentation: add pages to manual, add minimal docstrings for fft,
+ digital_filter, and generated objects, improve specdesc documentation
+ * filterbank: add get_norm/power documentation
+ * source: take a copy of the last frame before resizing it, raise an
+ exception when read failed, fix compilation warning
+ * fixes: remove unneeded check convert with PyFloat_FromDouble or
+ PyFloat_FromDouble, check if sink, digital_filter, were created before
+ deleting
+
+ [ Tests ]
+
+ * python/tests/: switch to pytest (slightly slower than nose2 but better at
+ capturing warnings and parametrization), improve coding style and coverage.
+ Tests should now be run with `pytest`.
+ * tests/: Each test program in C must now return 0, otherwise the test will
+ fail. Examples have been modified to run themselves on a test audio file,
+ but can still be run with arguments. Tests for `source` and `sink` have been
+ factorised, and some code cleaning. A python script is used to create a
+ test sound file. Tested on linux, macos, and windows, improvements to
+ test-mfcc (closes gh-219).
+
+ [ Build system ]
+
+ * waf: upgrade to 2.0.14, check the return code of each test program,
+ update rules to build manual and api documentation into build/, check
+ for errno.h
+ * osx: use -Os in scripts/build_apple_frameworks
+ * Makefile: improve coverage reports
+ * appveyor, travis, circleci: switch to pytest, set one travis config to use
+ sndfile only
+ * travis: add py3.6, drop py3.4, use py3.5 to test debug mode
+ * azure: add basic configuration
+
+2018-11-21 Paul Brossier <piem@aubio.org>
+
+ [ Overview ]
+
+ * VERSION: bump to 0.4.8
+ * notes: new option release_drop (gh-203)
+ * spectral: new parameters added to filterbank and mfcc (gh-206)
+ * python: start documenting module (gh-73, debian #480018), improve build for
+ win-amd64 (gh-154, gh-199, gh-208)
+ * fixes: prevent crash when using fft sizes unsupported by vDSP (gh-207),
+ prevent saturation when down-mixing a multi-channel source (avcodec/ffmpeg)
+
+ [ Fixes ]
+
+ * avcodec: prevent saturation when down-mixing a multi-channel source, emit
+ a warning if compiling against avutil < 53 (gh-137), wrap long lines
+ * examples/: avoid hiding global and unreachable code
+ * fft: limit to r*2*n sizes, with r in [1, 3, 5, 15] (vDSP only) (gh-207)
+ * fft: fix reconstruction for odd sizes (fftw only)
+ * pvoc: add missing implementations for aubio_pvoc_get_hop/win
+ * mathutils: increase ln(2) precision of in freqtomidi/miditofreq
+ * wavetable: stop sets playing to 0, add dummy implementation for _load
+
+ [ New features ]
+
+ * src/musicutils.h: new aubio_meltohz, aubio_hztomel, with _htk versions
+ * src/spectral/filterbank.h: new set_mel_coeffs, set_mel_coeffs_htk,
+ set_power, and set_norm methods, improved set_triangle_bands
+ * src/spectral/mfcc.h: new set_scale, set_power, set_norm, set_mel_coeffs,
+ set_mel_coeffs_htk, set_mel_coeffs_slaney
+ * src/mathutils.h: new fvec_mul
+ * src/notes: new option release_drop to prevent missing note-offs (gh-203)
+
+ [ Python module ]
+
+ * fix: rounding to nearest integer in midi2note and freq2note
+ * general: supports code generation of setters with none or multiple
+ parameters
+ * documentation: add docstrings do fvec, cvec, source, sink, pvoc, frequency
+ conversion and level detection routines (gh-73, debian #480018)
+ * slicing: improve and document slice_source_at_stamps
+ * module: new note2freq function, recover error log when raising exceptions
+ on failed set_ methods, prevent cyclic import, coding style improvements
+ * demos: improve coding style, fix bpm_extract arguments
+ * MANIFEST.in: exclude *.pyc, improve patterns
+
+ [ Documentation ]
+
+ * doc/: use sphinx autodoc to load docstrings from aubio module, reorganize
+ python module documentation, add a note about double precision, use https
+ when possible
+ * src/spectral/: update Auditory Toolbox url, update copyright year
+
+ [ Tools ]
+
+ * aubionotes: add --release-drop option
+ * aubio: add --release-drop and --silence options to `aubio notes`,
+ workaround for -V to really show version (py2)
+ * aubiocut: add option --create-first to always create first slice
+
+ [ Tests ]
+
+ * tests/, python/tests: add tests for new methods, check source channel
+ down-mix, improve coverage
+
+ [ Build system ]
+
+ * Makefile: disable docs when measuring coverage, add branch coverage
+ option, add coverage_zero_counters target, improve html report
+ * waf: update to 2.0.12, improve wscript style, prevent shipping some
+ generated files
+ * python: always show compiler warnings when pre-processing headers,
+ workaround to fix code generation for win-amd64 (gh-154, gh-199, gh-208).
+ * continuous integration: add azure pipelines, update and improve
+ configurations for appveyor, circleci, and travis.
+
+2018-09-22 Paul Brossier <piem@aubio.org>
+
+ [ Overview ]
+
+ * VERSION: bump to 0.4.7
+ * src/spectral/dct.h: add dct type II object with optimised versions
+ * src/io/, src/notes/, src/pitch: prevent crashes on corrupted files
+ * examples/: fix jack midi output, improve messages when jack disabled
+ * python/: add dct support, minor bug fixes tests and demos
+ * wscript: improve support for BLAS/ATLAS
+
+ [ Library fixes ]
+
+ * src/pitch/pitchyinfft.c: fix out of bound read when samplerate > 50kHz
+ thanks to @fCorleone (closes #189, CVE-2018-14523, debian #904906)
+ * src/notes/notes.c: bail out if pitch creation failed (see #188)
+ * src/io/source_wavread.c:
+ - also exit if samplerate is negative (closes #188, CVE-2018-14522,
+ debian #904907)
+ - add some input validation (closes #148 and #158, CVE-2017-17054,
+ debian #883355)
+ * src/io/source_avcodec.c:
+ - give up if resampling context failed opening (see #137, closes #187,
+ CVE-2018-14521, debian #904908)
+ - give up reading file if number of channel changes during stream (closes
+ #137, CVE-2017-17554, debian #884237)
+ - make sure libavutil > 52 before checking avFrame->channels (see #137)
+ - fix build with ffmpeg 4.0, thanks to @jcowgill (closes #168, #173)
+ - avoid deprecated call for ffmpeg >= 4.0
+ * src/onset/onset.c: add dummy default parameters for wphase (closes #150)
+
+ [ Tools ]
+
+ * examples/parse_args.h: hide jack options if not available, improve error
+ message (closes #182)
+ * examples/utils.h: process_block returns void
+ * examples/utils.c: fix examples failing to send more than one JACK midi
+ event per frame, thanks to @cyclopsian (closes #201)
+
+ [ New features ]
+
+ * src/spectral/dct.h: add dct type II object with implementation factory
+ * src/spectral/dct_plain.c: add plain dct implementation
+ * src/spectral/dct_ooura.c: add ooura implementation
+ * src/spectral/dct_fftw.c: add fftw implementation
+ * src/spectral/dct_ipp.c: add ipp version
+ * src/spectral/dct_accelerate.c: add vdsp/accelerate dct
+ * tests/src/spectral/test-dct.c: check reconstruction works
+ * src/spectral/mfcc.c: use new dct to compute mfcc
+
+ [ Library internals ]
+
+ * src/aubio_priv.h: avoid hard-coded undefs, split BLAS and ATLAS support,
+ add vdsp scalar add and multiply
+
+ [ Build system ]
+
+ * wscript:
+ - add options to disable examples and tests
+ - detect includes for openblas/libblas/atlas
+ * scripts/get_waf.sh: bump to 2.0.11, verify signature if gpg available
+ * python/lib/gen_external.py: pass '-x c' to emcc only
+
+ [ Python ]
+
+ * python/lib/gen_code.py: add support for rdo methods
+ * python/tests/test_dct.py: add tests for new dct
+ * python/demos/demo_pitch_sinusoid.py: use // to yield an integer, fixing
+ demo on py3, thanks to @ancorcruz (closes #176)
+ * python/ext/py-musicutils.*: add shift(fvec) and ishift(fvec)
+ * python/tests/test_fvec_shift.py: add tests for shift() and ishift()
+ * python/lib/aubio/cmd.py: fix typo in comment
+
+ [ Documentation ]
+
+ * README.md, doc/statuslinks.rst: use latest for commits-since
+ * examples/parse_args.h: add yinfast to pitch algorithms
+ * doc/requirements.rst: add some blas documentation
+ * doc/requirements.rst: split media/optimisation libraries
+ * doc/develop.rst: fixed spelling error, thanks to Jon Williams (closes #161)
+ * doc/aubio{pitch,notes}.txt: add yinfast to list of pitch methods
+
+ [ Continuous integration ]
+
+ * .travis.yml: remove xcode8.2 builds, group osx, add alias pip=pip2
+ * .appveyor.yml: upgrade pip first, always use python -m pip
+
2017-10-02 Paul Brossier <piem@aubio.org>
[ Overview ]
include AUTHORS COPYING README.md VERSION ChangeLog
include python/README.md
include this_version.py
+include waf_gensyms.py
+include waf
+recursive-include waflib *.py
include Makefile wscript */wscript_build
-include waf waflib/* waflib/*/*
-exclude waflib/__pycache__/*
include aubio.pc.in
-include nose2.cfg
include requirements.txt
include src/*.c src/*.h
include src/*/*.c src/*/*.h
include examples/*.c examples/*.h
-include tests/*.h tests/*/*.c tests/*/*/*.c
+recursive-include tests *.h *.c *.py
include python/ext/*.h
-include python/__init__.py
-include python/lib/__init__.py
-include python/lib/moresetuptools.py
-include python/lib/gen_external.py
-include python/lib/gen_code.py
-include python/tests/run_all_tests
-include python/tests/*.py
+recursive-include python *.py
+include python/README.md
include python/tests/eval_pitch
-include python/demos/*.py
include python/tests/*.expected
include doc/*.txt doc/*.rst doc/*.cfg doc/Makefile doc/make.bat doc/conf.py
exclude doc/full.cfg
DATAROOTDIR?=$(PREFIX)/share
MANDIR?=$(DATAROOTDIR)/man
-# default nose2 command
-NOSE2?=nose2 -N 4 --verbose
+# default python test command
+PYTEST?=pytest --verbose
SOX=sox
TESTSOUNDS := python/tests/sounds
+LCOVOPTS += --rc lcov_branch_coverage=1
+
all: build
checkwaf:
test_python: export PYTHONPATH=$(PYDESTDIR)/$(LIBDIR)
test_python: local_dylib
# run test with installed package
- # ./python/tests/run_all_tests --verbose
- # run with nose2, multiple processes
- $(NOSE2)
+ $(PYTEST)
clean_python:
./setup.py clean
uninstall_python \
check_clean_python
+coverage_cycle: coverage_zero_counters coverage_report
+
+coverage_zero_counters:
+ lcov --zerocounters --directory .
+
coverage: export CFLAGS=--coverage
coverage: export LDFLAGS=--coverage
coverage: export PYTHONPATH=$(PWD)/python/lib
coverage: export LD_LIBRARY_PATH=$(PWD)/build/src
coverage: force_uninstall_python deps_python \
clean_python clean distclean build local_dylib
- lcov --capture --no-external --directory . --output-file build/coverage_lib.info
+ # capture coverage after running c tests
+ lcov $(LCOVOPTS) --capture --no-external --directory . \
+ --output-file build/coverage_lib.info
+ # build and test python
pip install -v -e .
- coverage run `which nose2`
- lcov --capture --no-external --directory . --output-file build/coverage_python.info
- lcov -a build/coverage_python.info -a build/coverage_lib.info -o build/coverage.info
-
+ # run tests, with python coverage
+ coverage run `which pytest`
+ # capture coverage again
+ lcov $(LCOVOPTS) --capture --no-external --directory . \
+ --output-file build/coverage_python.info
+ # merge both coverage info files
+ lcov $(LCOVOPTS) -a build/coverage_python.info -a build/coverage_lib.info \
+ --output-file build/coverage.info
+ # remove tests
+ lcov $(LCOVOPTS) --remove build/coverage.info '*/ooura_fft8g*' \
+ --output-file build/coverage_lib.info
+
+# make sure we don't build the doc, which builds a temporary python module
+coverage_report: export WAFOPTS += --disable-docs
coverage_report: coverage
- genhtml build/coverage.info --output-directory lcov_html
- mkdir -p gcovr_html/
- gcovr -r . --html --html-details \
- --output gcovr_html/index.html \
- --exclude ".*tests/.*" --exclude ".*examples/.*"
+ # generate report with lcov's genhtml
+ genhtml build/coverage_lib.info --output-directory build/coverage_c \
+ --branch-coverage --highlight --legend
+ # generate python report with coverage python package
coverage report
- coverage html
+ coverage html -d build/coverage_python
+ # show links to generated reports
+ for i in $$(ls build/coverage_*/index.html); do echo file://$(PWD)/$$i; done
sphinx: configure
$(WAFCMD) sphinx $(WAFOPTS)
[](https://travis-ci.org/aubio/aubio "Travis build status")
[](https://ci.appveyor.com/project/piem/aubio "Appveyor build status")
-[](https://landscape.io/github/aubio/aubio/master "Landscape code health")
-[](https://github.com/aubio/aubio "Commits since last release")
+[](https://github.com/aubio/aubio "Commits since last release")
[](http://aubio.readthedocs.io/en/latest/?badge=latest "Latest documentation")
[](https://zenodo.org/badge/latestdoi/396389)
AUBIO_MAJOR_VERSION=0
-AUBIO_MINOR_VERSION=4
-AUBIO_PATCH_VERSION=7
+AUBIO_MINOR_VERSION=5
+AUBIO_PATCH_VERSION=0
AUBIO_VERSION_STATUS='~alpha'
LIBAUBIO_LT_CUR=5
-LIBAUBIO_LT_REV=3
-LIBAUBIO_LT_AGE=7
+LIBAUBIO_LT_REV=4
+LIBAUBIO_LT_AGE=8
--- /dev/null
+# configuration file for azure continuous integration
+jobs:
+
+- job: linux
+ pool:
+ vmImage: 'ubuntu-16.04'
+ steps:
+ - script: |
+ make
+ displayName: 'make'
+ env:
+ CFLAGS: -Werror
+
+- job: windows
+ pool:
+ vmImage: 'vs2017-win2016'
+ steps:
+ - script: |
+ make
+ displayName: 'make'
+ env:
+ # fail on error
+ CFLAGS: /WX
+
+- job: macos
+ pool:
+ vmImage: 'macos-10.13'
+ steps:
+ - script: |
+ brew update
+ brew install pkg-config gnupg
+ brew install sox ffmpeg libsndfile lcov
+ displayName: 'brew install'
+ - script: |
+ make
+ displayName: 'make'
+ env:
+ CFLAGS: -Werror
+++ /dev/null
-dependencies:
- pre:
- - sudo apt-get update; sudo apt-get install make sox pkg-config libavcodec-dev libavformat-dev libavresample-dev libavutil-dev libsndfile1-dev libsamplerate-dev
-
-test:
- pre:
- - make create_test_sounds
- override:
- - nose2 -v
License
-------
-aubio is a `free <http://www.debian.org/intro/free>`_ and `open source
+aubio is a `free <https://www.debian.org/intro/free>`_ and `open source
<http://www.opensource.org/docs/definition.php>`_ software; **you** can
redistribute it and/or modify it under the terms of the `GNU
<https://www.gnu.org/>`_ `General Public License
NOTES
- The "note" command accepts all common options and no additional options.
+ The following additional options can be used with the "notes" subcommand.
+
+ -s <value>, --silence <value> silence threshold, in dB (default: -70)
+
+ -d <value>, --release-drop <value> release drop level, in dB. If the level
+ drops more than this amount since the last note started, the note will be
+ turned off (default: 10).
MFCC
--cut-until-nslices n How many extra slices should be added at the end of
each slice (default 0).
+ --create-first Alway create first slice.
+
-h, --help Print a short help message and exit.
-v, --verbose Be verbose.
according to Malcolm Slaney's Auditory Toolbox, available at the following
url:
- http://cobweb.ecn.purdue.edu/~malcolm/interval/1998-010/ (see file mfcc.m)
+ https://engineering.purdue.edu/~malcolm/interval/1998-010/ (see file mfcc.m)
SEE ALSO
aubionotes source
aubionotes [[-i] source]
[-r rate] [-B win] [-H hop]
- [-O method] [-t thres]
+ [-O method] [-t thres] [-d drop]
[-p method] [-u unit] [-l thres]
[-T time-format]
[-s sil]
will not be detected. A value of -20.0 would eliminate most onsets but the
loudest ones. A value of -90.0 would select all onsets. Defaults to -90.0.
+ -d, --release-drop Set the release drop threshold, in dB. If the level drops
+ more than this amount since the last note started, the note will be turned
+ off. Defaults to 10.
+
-T, --timeformat format Set time format (samples, ms, seconds). Defaults to
seconds.
PITCH METHODS
- Available methods: default, schmitt, fcomb, mcomb, specacf, yin, yinfft.
+ Available methods: default, schmitt, fcomb, mcomb, specacf, yin, yinfft,
+ yinfast.
See aubiopitch(1) for details about these methods.
Chapter 3, Pitch Analysis, PhD thesis, Centre for Digital music, Queen Mary
University of London, London, UK, 2006.
+ yinfast YIN algorithm (accelerated)
+
+ An optimised implementation of the YIN algorithm, yielding results identical
+ to the original YIN algorithm, while reducing its computational cost from
+ O(n^2) to O(n log(n)).
+
SEE ALSO
aubioonset(1),
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.viewcode', 'sphinx.ext.autodoc']
+extensions = ['sphinx.ext.viewcode', 'sphinx.ext.autodoc',
+ 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx']
+
+autodoc_member_order = 'groupwise'
+
+intersphinx_mapping = {
+ 'numpy': ('https://docs.scipy.org/doc/numpy/', None),
+ }
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# General information about the project.
project = u'aubio'
-copyright = u'2016, Paul Brossier'
+copyright = u'2018, Paul Brossier'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
https://anonscm.debian.org/cgit/collab-maint/aubio.git/. Use
``git-buildpackage`` to build from the git repository. For instance:
-.. code-block:: bash
+.. code-block:: console
$ git clone git://anonscm.debian.org/collab-maint/aubio.git
$ cd aubio
.. _develop:
-Developping with aubio
-======================
+Developing with aubio
+=====================
Here is a brief overview of the C library.
installing
python_module
+ python
cli
develop
about
./waf build
sudo ./waf install
-- :ref:`install python-aubio from source <python>`::
+- :ref:`install python-aubio from source <python-install>`::
# from git
pip install git+https://git.aubio.org/aubio/aubio/
cd aubio
pip install -v .
-- :ref:`install python-aubio from a pre-compiled binary <python>`::
+- :ref:`install python-aubio from a pre-compiled binary <python-install>`::
# conda [osx, linux, win]
conda install -c conda-forge aubio
--- /dev/null
+.. currentmodule:: aubio
+.. default-domain:: py
+
+Analysis
+--------
+
+.. members of generated classes are not shown
+
+.. autoclass:: onset
+
+.. autoclass:: pitch
+
+.. autoclass:: tempo
+
+.. autoclass:: notes
--- /dev/null
+.. default-domain:: py
+.. currentmodule:: aubio
+
+Data-types
+----------
+
+This section contains the documentation for :data:`float_type`,
+:class:`fvec`, and :class:`cvec`.
+
+.. defined in rst only
+
+.. data:: float_type
+
+ A string constant describing the floating-point representation used in
+ :class:`fvec`, :class:`cvec`, and elsewhere in this module.
+
+ Defaults to `"float32"`.
+
+ If `aubio` was built specifically with the option `--enable-double`, this
+ string will be defined to `"float64"`. See :ref:`py-doubleprecision` in
+ :ref:`python-install` for more details on building aubio in double
+ precision mode.
+
+ .. rubric:: Examples
+
+ >>> aubio.float_type
+ 'float32'
+ >>> numpy.zeros(10).dtype
+ 'float64'
+ >>> aubio.fvec(10).dtype
+ 'float32'
+ >>> np.arange(10, dtype=aubio.float_type).dtype
+ 'float32'
+
+.. defined in `python/lib/aubio/__init__.py`
+
+.. autoclass:: fvec
+ :members:
+
+.. defined in `python/ext/py-cvec.h`
+
+.. autoclass:: cvec
+ :members:
--- /dev/null
+.. default-domain:: py
+.. currentmodule:: aubio
+
+Examples
+--------
+
+Below is a short selection of examples using the aubio module.
+
+Read a sound file
+.................
+
+Here is a simple script, :download:`demo_source_simple.py
+<../python/demos/demo_source_simple.py>` that reads all the samples from a
+media file using :class:`source`:
+
+.. literalinclude:: ../python/demos/demo_source_simple.py
+ :language: python
+
+Filter a sound file
+...................
+
+Here is another example, :download:`demo_filter.py
+<../python/demos/demo_filter.py>`, which applies a filter to a sound file
+and writes the filtered signal in another file:
+
+* read audio samples from a file with :class:`source`
+
+* filter them using an `A-weighting <https://en.wikipedia.org/wiki/A-weighting>`_
+ filter using :class:`digital_filter`
+
+* write the filtered samples to a new file with :class:`sink`.
+
+.. literalinclude:: ../python/demos/demo_filter.py
+ :language: python
+
+More examples
+.............
+
+For more examples showing how to use other components of the module, see
+the `python demos folder`_.
+
+.. _python demos folder: https://github.com/aubio/aubio/blob/master/python/demos
--- /dev/null
+.. currentmodule:: aubio
+.. default-domain:: py
+
+Input/Output
+------------
+
+This section contains the documentation for two classes:
+:class:`source`, to read audio samples from files, and :class:`sink`,
+to write audio samples to disk.
+
+.. defined in `python/ext`
+
+..
+ Note: __call__ docstrings of objects defined in C must be written
+ specifically in RST, since there is no known way to add them to
+ their C implementation.
+
+..
+ TODO: remove special-members documentation
+
+.. defined in py-source.c
+
+.. autoclass:: source
+ :members:
+ :special-members: __enter__
+ :no-special-members:
+
+ .. function:: __call__()
+
+ Read at most `hop_size` new samples from self, return them in
+ a tuple with the number of samples actually read.
+
+ The returned tuple contains:
+
+ - a vector of shape `(hop_size,)`, filled with the `read` next
+ samples available, zero-padded if `read < hop_size`
+ - `read`, an integer indicating the number of samples read
+
+ If opened with more than one channel, the frames will be
+ down-mixed to produce the new samples.
+
+ :returns: A tuple of one array of samples and one integer.
+ :rtype: (array, int)
+
+ .. seealso:: :meth:`__next__`
+
+ .. rubric:: Example
+
+ >>> src = aubio.source('stereo.wav')
+ >>> while True:
+ ... samples, read = src()
+ ... if read < src.hop_size:
+ ... break
+
+ .. function:: __next__()
+
+ Read at most `hop_size` new frames from self, return them in
+ an array.
+
+ If source was opened with one channel, next(self) returns
+ an array of shape `(read,)`, where `read` is the actual
+ number of frames read (`0 <= read <= hop_size`).
+
+ If `source` was opened with more then one channel, the
+ returned arrays will be of shape `(channels, read)`, where
+ `read` is the actual number of frames read (`0 <= read <=
+ hop_size`).
+
+ :return: A tuple of one array of frames and one integer.
+ :rtype: (array, int)
+
+ .. seealso:: :meth:`__call__`
+
+ .. rubric:: Example
+
+ >>> for frames in aubio.source('song.flac')
+ ... print(samples.shape)
+
+ .. function:: __iter__()
+
+ Implement iter(self).
+
+ .. seealso:: :meth:`__next__`
+
+ .. function:: __enter__()
+
+ Implement context manager interface. The file will be opened
+ upon entering the context. See `with` statement.
+
+ .. rubric:: Example
+
+ >>> with aubio.source('loop.ogg') as src:
+ ... src.uri, src.samplerate, src.channels
+
+ .. function:: __exit__()
+
+ Implement context manager interface. The file will be closed
+ before exiting the context. See `with` statement.
+
+ .. seealso:: :meth:`__enter__`
+
+.. py-sink.c
+ TODO: remove special-members documentation
+
+.. autoclass:: aubio.sink
+ :members:
+
+ .. function:: __call__(vec, length)
+
+ Write `length` samples from `vec`.
+
+ :param array vec: input vector to write from
+ :param int length: number of samples to write
+ :example:
+
+ >>> with aubio.sink('foo.wav') as snk:
+ ... snk(aubio.fvec(1025), 1025)
+
--- /dev/null
+.. currentmodule:: aubio
+.. default-domain:: py
+
+.. members of generated classes are not yet documented
+
+Spectral features
+-----------------
+
+This section contains the documentation for:
+
+- :class:`dct`
+- :class:`fft`
+- :class:`filterbank`
+- :class:`mfcc`
+- :class:`pvoc`
+- :class:`specdesc`
+- :class:`tss`
+
+.. autoclass:: dct
+
+.. autoclass:: fft
+ :members:
+
+.. autoclass:: filterbank
+ :members:
+
+.. autoclass:: mfcc
+
+.. autoclass:: pvoc
+ :members:
+
+.. autoclass:: specdesc
+
+.. autoclass:: tss
--- /dev/null
+.. currentmodule:: aubio
+.. default-domain:: py
+
+Synthesis
+---------
+
+.. autoclass:: sampler
+
+.. autoclass:: wavetable
--- /dev/null
+.. currentmodule:: aubio
+.. default-domain:: py
+
+Digital filters
+---------------
+
+.. autoclass:: digital_filter
+ :members:
--- /dev/null
+.. default-domain:: py
+.. currentmodule:: aubio
+
+Utilities
+---------
+
+This section documents various helper functions included in the aubio library.
+
+Note name conversion
+....................
+
+.. midiconv.py
+
+.. autofunction:: note2midi
+
+.. autofunction:: midi2note
+
+.. autofunction:: freq2note
+
+.. autofunction:: note2freq
+
+Frequency conversion
+....................
+
+.. python/ext/ufuncs.c
+
+.. autofunction:: freqtomidi
+
+.. autofunction:: miditofreq
+
+.. python/ext/py-musicutils.h
+
+.. autofunction:: meltohz
+
+.. autofunction:: hztomel
+
+.. python/ext/aubiomodule.c
+
+.. autofunction:: bintomidi
+.. autofunction:: miditobin
+.. autofunction:: bintofreq
+.. autofunction:: freqtobin
+
+Audio file slicing
+..................
+
+.. slicing.py
+
+.. autofunction:: slice_source_at_stamps
+
+Windowing
+.........
+
+.. python/ext/py-musicutils.h
+
+.. autofunction:: window
+
+Audio level detection
+.....................
+
+.. python/ext/py-musicutils.h
+
+.. autofunction:: level_lin
+.. autofunction:: db_spl
+.. autofunction:: silence_detection
+.. autofunction:: level_detection
+
+Vector utilities
+................
+
+.. python/ext/aubiomodule.c
+
+.. autofunction:: alpha_norm
+.. autofunction:: zero_crossing_rate
+.. autofunction:: min_removal
+
+.. python/ext/py-musicutils.h
+
+.. autofunction:: shift
+.. autofunction:: ishift
+
+.. python/ext/ufuncs.c
+
+.. autofunction:: unwrap2pi
--- /dev/null
+.. make sure our default-domain is python here
+.. default-domain:: py
+
+.. set current module
+.. currentmodule:: aubio
+
+..
+ we follow numpy type docstrings, see:
+ https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard
+..
+ note: we do not import aubio's docstring, which will be displayed from an
+ interpreter.
+
+.. .. automodule:: aubio
+
+
+.. _python:
+
+Python documentation
+====================
+
+This module provides a number of classes and functions for the analysis of
+music and audio signals.
+
+Contents
+--------
+
+.. toctree::
+ :maxdepth: 1
+
+ py_datatypes
+ py_io
+ py_temporal
+ py_spectral
+ py_analysis
+ py_synth
+ py_utils
+ py_examples
+
+Introduction
+------------
+
+This document provides a reference guide. For documentation on how to
+install aubio, see :ref:`python-install`.
+
+Examples included in this guide and within the code are written assuming
+both `aubio` and `numpy`_ have been imported:
+
+.. code-block:: python
+
+ >>> import aubio
+ >>> import numpy as np
+
+`Changed in 0.4.8` : Prior to this version, almost no documentation was
+provided with the python module. This version adds documentation for some
+classes, including :class:`fvec`, :class:`cvec`, :class:`source`, and
+:class:`sink`.
+
+.. _numpy: https://www.numpy.org
-.. _python:
+.. _python-install:
-Python module
-=============
+Installing aubio for Python
+===========================
-The aubio extension for Python is available for Python 2.7 and Python 3.
+aubio is available as a package for Python 2.7 and Python 3. The aubio
+extension is written C using the `Python/C`_ and the `Numpy/C`_ APIs.
+
+.. _Python/C: https://docs.python.org/c-api/index.html
+.. _Numpy/C: https://docs.scipy.org/doc/numpy/reference/c-api.html
+
+For general documentation on how to install Python packages, see `Installing
+Packages`_.
Installing aubio with pip
-------------------------
-aubio can now be installed using ``pip``:
+aubio can be installed from `PyPI`_ using ``pip``:
-.. code-block:: bash
+.. code-block:: console
$ pip install aubio
-Building the module
--------------------
+See also `Installing from PyPI`_ for general documentation.
-From ``aubio`` source directory, run the following:
+.. note::
-.. code-block:: bash
+ aubio is currently a `source only`_ package, so you will need a compiler to
+ install it from `PyPI`_. See also `Installing aubio with conda`_ for
+ pre-compiled binaries.
- $ ./setup.py clean
- $ ./setup.py build
- $ sudo ./setup.py install
+.. _PyPI: https://pypi.python.org/pypi/aubio
+.. _Installing Packages: https://packaging.python.org/tutorials/installing-packages/
+.. _Installing from PyPI: https://packaging.python.org/tutorials/installing-packages/#installing-from-pypi
+.. _source only: https://packaging.python.org/tutorials/installing-packages/#source-distributions-vs-wheels
+
+Installing aubio with conda
+---------------------------
-Using aubio in python
----------------------
+`Conda packages`_ are available through the `conda-forge`_ channel for Linux,
+macOS, and Windows:
-Once you have python-aubio installed, you should be able to run ``python -c
-"import aubio; print(aubio.version)"``.
+.. code-block:: console
-A simple example
-................
+ $ conda config --add channels conda-forge
+ $ conda install -c conda-forge aubio
-Here is a :download:`simple script <../python/demos/demo_source_simple.py>`
-that reads all the samples from a media file:
+.. _Conda packages: https://anaconda.org/conda-forge/aubio
+.. _conda-forge: https://conda-forge.org/
-.. literalinclude:: ../python/demos/demo_source_simple.py
- :language: python
+.. _py-doubleprecision:
-Filtering an input sound file
-.............................
+Double precision
+----------------
-Here is a more complete example, :download:`demo_filter.py
-<../python/demos/demo_filter.py>`. This files executes the following:
+This module can be compiled in double-precision mode, in which case the
+default type for floating-point samples will be 64-bit. The default is
+single precision mode (32-bit, recommended).
-* read an input media file (``aubio.source``)
+To build the aubio module with double precision, use the option
+`--enable-double` of the `build_ext` subcommand:
+
+.. code:: bash
+
+ $ ./setup.py clean
+ $ ./setup.py build_ext --enable-double
+ $ pip install -v .
-* filter it using an `A-weighting <https://en.wikipedia.org/wiki/A-weighting>`_
- filter (``aubio.digital_filter``)
+**Note**: If linking against `libaubio`, make sure the library was also
+compiled in :ref:`doubleprecision` mode.
-* write result to a new file (``aubio.sink``)
-.. literalinclude:: ../python/demos/demo_filter.py
- :language: python
+Checking your installation
+--------------------------
-More demos
-..........
+Once the python module is installed, its version can be checked with:
+
+.. code-block:: console
+
+ $ python -c "import aubio; print(aubio.version, aubio.float_type)"
+
+The command line `aubio` is also installed:
+
+.. code-block:: console
+
+ $ aubio -h
-Check out the `python demos folder`_ for more examples.
Python tests
------------
-A number of `python tests`_ are provided. To run them, use
-``python/tests/run_all_tests``.
+A number of Python tests are provided in the `python/tests`_ folder. To run
+them, install `pytest`_ and run it from the aubio source directory:
+
+.. code-block:: console
-.. _python demos folder: https://github.com/aubio/aubio/blob/master/python/demos
-.. _demo_filter.py: https://github.com/aubio/aubio/blob/master/python/demos/demo_filter.py
-.. _python tests: https://github.com/aubio/aubio/blob/master/python/tests
+ $ pip install pytest
+ $ git clone https://git.aubio.org/aubio/aubio
+ $ cd aubio
+ $ pytest
+.. _python/tests: https://github.com/aubio/aubio/blob/master/python/tests
+.. _pytest: https://pytest.org
If ``pkg-config`` is not found in ``PATH``, the configure step will
succeed, but none of the external libraries will be used.
+Media libraries
+---------------
+
libav
.....
then fail if the required library is not found. To disable this option,
configure with ``--disable-samplerate``
+Optimisation libraries
+----------------------
+
libfftw3
........
then fail if the required library is not found. To disable this option,
configure with ``--disable-fftw3``
+blas
+....
+
+On macOs/iOS, `blas
+<https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms>`_ are made
+available through the Accelerate framework.
+
+On Linux, they can be enabled with ``--enable-blas``. On Debian (etch),
+`atlas`_, `openblas`_, and `libblas`_ have been successfully tested.
+
+When enabled, ``waf`` will check for the current blas configuration by running
+``pkg-config --libs blas``. Depending of the library path returned by
+``pkg-config``, different headers will be searched for.
+
+.. note::
+
+ On Debian systems, `multiple versions of BLAS and LAPACK
+ <https://wiki.debian.org/DebianScience/LinearAlgebraLibraries>`_ can be
+ installed. To configure which libblas is being used:
+
+ .. code-block:: console
+
+ $ sudo update-alternatives --config libblas.so
+
+..
+ Expected pkg-config output for each alternative:
+ /usr/lib/atlas-base/atlas/libblas.so
+ -L/usr/lib/atlas-base/atlas -lblas
+ /usr/lib/openblas-base/libblas.so
+ -L/usr/lib/openblas-base -lblas
+ /usr/lib/libblas/libblas.so
+ -lblas
+
+atlas
+.....
+
+`ATLAS BLAS APIs <http://math-atlas.sourceforge.net/>`_ will be used the path
+returned by ``pkg-config --libs blas`` contains ``atlas``.
+
+..
+ ``<atlas/cblas.h>`` will be included.
+
+Example:
+
+.. code-block:: console
+
+ $ pkg-config --libs blas
+ -L/usr/lib/atlas-base/atlas -lblas
+ $ ./waf configure --enable-atlas
+ [...]
+ Checking for 'blas' : yes
+ Checking for header atlas/cblas.h : yes
+
+openblas
+........
+
+`OpenBlas libraries <https://www.openblas.net/>`_ will be used when the output
+of ``pkg-config --libs blas`` contains 'openblas',
+
+..
+ ``<openblas/cblas.h>`` will be included.
+
+Example:
+
+.. code-block:: console
+
+ $ pkg-config --libs blas
+ -L/usr/lib/openblas-base -lblas
+ $ ./waf configure --enable-atlas
+ [...]
+ Checking for 'blas' : yes
+ Checking for header openblas/cblas.h : yes
+
+libblas
+.......
+
+`Netlib's libblas (LAPACK) <https://www.netlib.org/lapack/>`_ will be used if
+no specific library path is specified by ``pkg-config``
+
+..
+ ``<cblas.h>`` will be included.
+
+Example:
+
+.. code-block:: console
+
+ $ pkg-config --libs blas
+ -lblas
+ $ ./waf configure --enable-atlas
+ [...]
+ Checking for 'blas' : yes
+ Checking for header cblas.h : yes
+
+
Platform notes
--------------
--manpagesdir=/opt/share/man \
uninstall clean distclean dist distcheck
+.. _doubleprecision:
+
Double precision
................
+The datatype used to store real numbers in aubio is named `smpl_t`. By default,
+`smpl_t` is defined as `float`, a `single-precision format
+<https://en.wikipedia.org/wiki/Single-precision_floating-point_format>`_
+(32-bit). Some algorithms require a floating point representation with a
+higher precision, for instance to prevent arithmetic underflow in recursive
+filters. In aubio, these special samples are named `lsmp_t` and defined as
+`double` by default (64-bit).
+
+Sometimes it may be useful to compile aubio in `double-precision`, for instance
+to reproduce numerical results obtained with 64-bit routines. In this case,
+`smpl_t` will be defined as `double`.
+
+The following table shows how `smpl_t` and `lsmp_t` are defined in single- and
+double-precision modes:
+
+.. list-table:: Single and double-precision modes
+ :align: center
+
+ * -
+ - single
+ - double
+ * - `smpl_t`
+ - ``float``
+ - ``double``
+ * - `lsmp_t`
+ - ``double``
+ - ``long double``
+
To compile aubio in double precision mode, configure with ``--enable-double``.
-To compile aubio in single precision mode, use ``--disable-double`` (default).
+To compile in single-precision mode (default), use ``--disable-double`` (or
+simply none of these two options).
Disabling the tests
...................
:target: https://ci.appveyor.com/project/piem/aubio/
:alt: Appveyor build status
-.. image:: https://landscape.io/github/aubio/aubio/master/landscape.svg?style=flat
- :target: https://landscape.io/github/aubio/aubio/master
- :alt: Landscape code health
-
.. image:: https://readthedocs.org/projects/aubio/badge/?version=latest
:target: https://aubio.readthedocs.io/en/latest/?badge=latest
:alt: Documentation status
-.. image:: https://img.shields.io/github/commits-since/aubio/aubio/0.4.6.svg?maxAge=2592000
+.. image:: https://img.shields.io/github/commits-since/aubio/aubio/latest.svg
:target: https://github.com/aubio/aubio
:alt: Commits since last release
goto beach;
}
- examples_common_process((aubio_process_func_t)process_block, process_print);
+ examples_common_process(process_block, process_print);
del_aubio_pvoc (pv);
del_cvec (fftgrain);
#include "utils.h"
#define PROG_HAS_PITCH 1
#define PROG_HAS_ONSET 1
+#define PROG_HAS_NOTES 1
#define PROG_HAS_SILENCE 1
#define PROG_HAS_JACK 1
// TODO add PROG_HAS_OUTPUT
silence_threshold);
}
}
+ if (release_drop != 10.) {
+ if (aubio_notes_set_release_drop (notes, release_drop) != 0) {
+ errmsg ("failed setting notes release drop to %.2f\n",
+ release_drop);
+ }
+ }
- examples_common_process((aubio_process_func_t)process_block, process_print);
+ examples_common_process(process_block, process_print);
// send a last note off if required
if (lastmidi) {
aubio_wavetable_set_freq ( wavetable, 2450.);
//aubio_sampler_load (sampler, "/archives/sounds/woodblock.aiff");
- examples_common_process((aubio_process_func_t)process_block, process_print);
+ examples_common_process(process_block, process_print);
// send a last note off
if (usejack) {
wavetable = new_aubio_wavetable (samplerate, hop_size);
aubio_wavetable_play ( wavetable );
- examples_common_process((aubio_process_func_t)process_block,process_print);
+ examples_common_process(process_block, process_print);
del_aubio_pitch (o);
del_aubio_wavetable (wavetable);
verbmsg ("using source: %s at %dHz\n", source_uri, samplerate);
verbmsg ("buffer_size: %d, ", buffer_size);
verbmsg ("hop_size: %d\n", hop_size);
- examples_common_process((aubio_process_func_t)process_block,process_print);
+ examples_common_process(process_block, process_print);
examples_common_del();
return 0;
}
aubio_wavetable_set_freq ( wavetable, 2450.);
//aubio_sampler_load (sampler, "/archives/sounds/woodblock.aiff");
- examples_common_process((aubio_process_func_t)process_block,process_print);
+ examples_common_process(process_block, process_print);
// send a last note off
if (usejack) {
extern char_t * tempo_method;
// more general stuff
extern smpl_t silence_threshold;
+extern smpl_t release_drop;
extern uint_t mix_input;
// midi tap
extern smpl_t miditap_note;
// internal stuff
extern int blocks;
-extern fvec_t *ibuf;
-extern fvec_t *obuf;
+extern fvec_t *input_buffer;
+extern fvec_t *output_buffer;
const char *prog_name;
#endif /* PROG_HAS_ONSET */
#ifdef PROG_HAS_PITCH
" -p --pitch select pitch detection algorithm\n"
- " <default|yinfft|yin|mcomb|fcomb|schmitt>; default=yinfft\n"
+ " <default|yinfft|yinfast|yin|mcomb|fcomb|schmitt>; default=yinfft\n"
" -u --pitch-unit select pitch output unit\n"
" <default|freq|hertz|Hz|midi|cent|bin>; default=freq\n"
" -l --pitch-tolerance select pitch tolerance\n"
" -s --silence select silence threshold\n"
" a value in dB, for instance -70, or -100; default=-90\n"
#endif /* PROG_HAS_SILENCE */
+#ifdef PROG_HAS_NOTES
+ " -d --release-drop select release drop threshold\n"
+ " a positive value in dB; default=10\n"
+#endif
" -T --time-format select time values output format\n"
" (samples, ms, seconds) default=seconds\n"
#ifdef PROG_HAS_OUTPUT
" -f --force-overwrite overwrite output file if needed\n"
" do not fail if output file already exists\n"
#endif /* PROG_HAS_OUTPUT */
-#ifdef PROG_HAS_JACK
+#if defined(PROG_HAS_JACK) && defined(HAVE_JACK)
" -j --jack use Jack\n"
#if defined(PROG_HAS_ONSET) && !defined(PROG_HAS_PITCH)
" -N --miditap-note MIDI note; default=69.\n"
" -V --miditap-velo MIDI velocity; default=65.\n"
#endif /* defined(PROG_HAS_ONSET) && !defined(PROG_HAS_PITCH) */
-#endif /* PROG_HAS_JACK */
+#endif /* defined(PROG_HAS_JACK) && defined(HAVE_JACK) */
" -v --verbose be verbose\n"
" -h --help display this message\n"
);
#ifdef PROG_HAS_SILENCE
"s:"
#endif /* PROG_HAS_SILENCE */
+#ifdef PROG_HAS_NOTES
+ "d:"
+#endif /* PROG_HAS_SILENCE */
#ifdef PROG_HAS_OUTPUT
"mf"
#endif /* PROG_HAS_OUTPUT */
#ifdef PROG_HAS_SILENCE
{"silence", 1, NULL, 's'},
#endif /* PROG_HAS_SILENCE */
+#ifdef PROG_HAS_NOTES
+ {"release-drop", 1, NULL, 'd'},
+#endif /* PROG_HAS_NOTES */
{"time-format", 1, NULL, 'T'},
#ifdef PROG_HAS_OUTPUT
{"mix-input", 0, NULL, 'm'},
{NULL, 0, NULL, 0}
};
#endif /* HAVE_GETOPT_H */
- prog_name = argv[0];
+ // better safe than sorry
if (argc < 1) {
usage (stderr, 1);
- return -1;
}
+ prog_name = argv[0];
#ifdef HAVE_GETOPT_H
do {
next_option = getopt_long (argc, argv, options, long_options, NULL);
case 's': /* silence threshold */
silence_threshold = (smpl_t) atof (optarg);
break;
+ case 'd': /* release-drop threshold */
+ release_drop = (smpl_t) atof (optarg);
+ break;
case 'm': /* mix_input flag */
mix_input = 1;
break;
usejack = 1;
#else
errmsg("Error: no arguments given (and no available audio input)\n");
- usage ( stderr, 1 );
+ errmsg(" consider recompiling with jack support (--enable-jack)\n");
+ exit ( 1 );
#endif /* HAVE_JACK */
#else
errmsg("Error: no arguments given\n");
char_t * tempo_method = "default";
// more general stuff
smpl_t silence_threshold = -90.;
+smpl_t release_drop = 10.;
uint_t mix_input = 0;
uint_t force_overwrite = 0;
// internal memory stuff
aubio_source_t *this_source = NULL;
aubio_sink_t *this_sink = NULL;
-fvec_t *ibuf;
-fvec_t *obuf;
+fvec_t *input_buffer;
+fvec_t *output_buffer;
smpl_t miditap_note = 69.;
smpl_t miditap_velo = 65.;
extern int parse_args (int argc, char **argv);
#if HAVE_JACK
+#define MAX_MIDI_EVENTS 128
+#define MAX_MIDI_EVENT_SIZE 3
aubio_jack_t *jack_setup;
jack_midi_event_t ev;
+jack_midi_data_t midi_data[MAX_MIDI_EVENTS * MAX_MIDI_EVENT_SIZE];
+size_t midi_event_count = 0;
#endif /* HAVE_JACK */
void examples_common_init (int argc, char **argv);
source_uri = "jack";
#endif /* HAVE_JACK */
}
- ibuf = new_fvec (hop_size);
- obuf = new_fvec (hop_size);
+ input_buffer = new_fvec (hop_size);
+ output_buffer = new_fvec (hop_size);
}
void examples_common_del (void)
{
-#ifdef HAVE_JACK
- if (ev.buffer) free(ev.buffer);
-#endif
- del_fvec (ibuf);
- del_fvec (obuf);
+ del_fvec (input_buffer);
+ del_fvec (output_buffer);
aubio_cleanup ();
fflush(stderr);
fflush(stdout);
if (usejack) {
#ifdef HAVE_JACK
- ev.size = 3;
- ev.buffer = malloc (3 * sizeof (jack_midi_data_t));
+ ev.size = MAX_MIDI_EVENT_SIZE;
ev.time = 0; // send it now
debug ("Jack activation ...\n");
aubio_jack_activate (jack_setup, process_func);
blocks = 0;
do {
- aubio_source_do (this_source, ibuf, &read);
- process_func (ibuf, obuf);
+ aubio_source_do (this_source, input_buffer, &read);
+ process_func (input_buffer, output_buffer);
// print to console if verbose or no output given
if (verbose || sink_uri == NULL) {
print();
}
if (this_sink) {
- aubio_sink_do (this_sink, obuf, hop_size);
+ aubio_sink_do (this_sink, output_buffer, hop_size);
}
blocks++;
total_read += read;
total_read, blocks, hop_size, source_uri, samplerate);
del_aubio_source (this_source);
- del_aubio_sink (this_sink);
+ if (this_sink)
+ del_aubio_sink (this_sink);
}
}
{
#ifdef HAVE_JACK
if (usejack) {
+ ev.buffer = midi_data + midi_event_count++ * MAX_MIDI_EVENT_SIZE;
+ if (midi_event_count >= MAX_MIDI_EVENTS) {
+ midi_event_count = 0;
+ }
ev.buffer[2] = velo;
ev.buffer[1] = pitch;
if (velo == 0) {
void send_noteon (smpl_t pitch, smpl_t velo);
/** common process function */
-typedef int (*aubio_process_func_t) (fvec_t * input, fvec_t * output);
+typedef void (*aubio_process_func_t) (fvec_t * input, fvec_t * output);
void process_block (fvec_t *ibuf, fvec_t *obuf);
void process_print (void);
+++ /dev/null
-[unittest]
-start-dir = python/tests/
-plugins = nose2.plugins.mp
-
-[multiprocess]
-always-on = false
-Python aubio module
-===================
+aubio
+=====
-This module wraps the aubio library for Python using the numpy module.
+aubio is a collection of tools for music and audio analysis.
-Using the Python aubio module
------------------------------
+This package integrates the aubio library with [NumPy] to provide a set of
+efficient tools to process and analyse audio signals, including:
-After installing python-aubio, you will be able to import the aubio module:
+- read audio from any media file, including videos and remote streams
+- high quality phase vocoder, spectral filterbanks, and linear filters
+- Mel-Frequency Cepstrum Coefficients and standard spectral descriptors
+- detection of note attacks (onset)
+- pitch tracking (fundamental frequency estimation)
+- beat detection and tempo tracking
- $ python
- [...]
- >>> import aubio
- >>> help(aubio.miditofreq)
+aubio works with both Python 2 and Python 3.
-Finding some inspiration
-------------------------
+Links
+-----
-Some examples are available in the `python/demos` directory. These scripts are
-small programs written in python and using python-aubio.
+- [module documentation][doc_python]
+- [installation instructions][doc_python_install]
+- [aubio manual][manual]
+- [aubio homepage][homepage]
+- [issue tracker][bugtracker]
-For instance, `demo_source.py` reads a media file.
+Demos
+-----
- $ ./python/demos/demo_source.py /path/to/sound/sample.wav
+Some examples are available in the [`python/demos` folder][demos_dir]. Each
+script is a command line program which accepts one ore more argument.
-and `demo_timestretch_online.py` stretches the original file into a new one:
+**Notes**: installing additional modules is required to run some of the demos.
- $ ./python/demo/demo_timestretch_online.py loop.wav stretched_loop.wav 0.92`
+### Analysis
-Note: you might need to install additional modules to run some of the demos.
-Some demos use [matplotlib](http://matplotlib.org/) to draw plots, others use
-[PySoundCard](https://github.com/bastibe/PySoundCard) to play and record
-sounds.
+- `demo_source.py` uses aubio to read audio samples from media files
+- `demo_onset_plot.py` detects attacks in a sound file and plots the results
+ using [matplotlib]
+- `demo_pitch.py` looks for fundamental frequency in a sound file and plots the
+ results using [matplotlib]
+- `demo_spectrogram.py`, `demo_specdesc.py`, `demo_mfcc.py` for spectral
+ analysis.
-Testing the Python module
--------------------------
+### Real-time
-Python tests are in `python/tests` and use the [nose2 python package][nose2].
+- `demo_pyaudio.py` and `demo_tapthebeat.py` use [pyaudio]
+- `demo_pysoundcard_play.py`, `demo_pysoundcard.py` use [PySoundCard]
+- `demo_alsa.py` uses [pyalsaaudio]
-To run the all the python tests, use the script:
+### Others
- $ ./python/tests/run_all_tests
+- `demo_timestretch.py` can change the duration of an input file and write the
+ new sound to disk,
+- `demo_wav2midi.py` detects the notes in a file and uses [mido] to write the
+ results into a MIDI file
-Each test script can also be called one at a time. For instance:
+### Example
- $ ./python/tests/test_note2midi.py -v
+Use `demo_timestretch_online.py` to slow down `loop.wav`, write the results in
+`stretched_loop.wav`:
-[nose2]: https://github.com/nose-devs/nose2
+ $ python demo_timestretch_online.py loop.wav stretched_loop.wav 0.92
-Install in a virtualenv
------------------------
-
-You should be able to install python-aubio directly from the top source
-directory of aubio.
-
-First, create a virtualenv to hold the required python module:
-
- $ virtualenv pyaubio
- $ source pyaubio/bin/activate
-
-Now install and build the python extension using:
-
- $ pip install .
-
-Install requirements
---------------------
-
-Before compiling this module, you must have compiled libaubio.
-
-A simple way to do this is with pip:
-
- $ pip install -r requirements.txt
-
-For more information about how this module works, please refer to the [Python/C
-API Reference Manual] (http://docs.python.org/c-api/index.html) and the
-[Numpy/C API Reference](http://docs.scipy.org/doc/numpy/reference/c-api.html).
-
-Compiling python aubio
-----------------------
-
-To build the aubio Python module, run the following command from the top source
-directory of aubio:
-
- $ ./setup.py build
-
-Note: if libaubio was previously built using waf, the script will use it.
-Otherwise, the entire library will be built inside the python extension.
-
-To find out more about `setup.py` options:
-
- $ ./setup.py --help
-
-Installing
+Built with
----------
-To install the Python module:
-
- $ ./setup.py install
-
-Alternatively, you may want to use the Python module without installing it by
-setting your PYTHONPATH, for instance as follows:
-
- $ export PYTHONPATH=$PYTHONPATH:$PWD/`ls -rtd build/lib.* | head -1`:$PWD/tests
-
+The core of aubio is written in C for portability and speed. In addition to
+[NumPy], aubio can be optionally built to use one or more of the following
+libraries:
+
+- media file reading:
+
+ - [ffmpeg] / [avcodec] to decode and read audio from almost any format,
+ - [libsndfile] to read audio from uncompressed sound files,
+ - [libsamplerate] to re-sample audio signals,
+ - [CoreAudio] to read all media formats supported by macOS, iOS, and tvOS.
+
+- hardware acceleration:
+
+ - [Atlas] and [Blas], for accelerated vector and matrix computations,
+ - [fftw3], to compute fast Fourier Transforms of any size,
+ - [Accelerate] for accelerated FFT and matrix computations (macOS/iOS),
+ - [Intel IPP], accelerated vector computation and FFT implementation.
+
+[ffmpeg]: https://ffmpeg.org
+[avcodec]: https://libav.org
+[libsndfile]: http://www.mega-nerd.com/libsndfile/
+[libsamplerate]: http://www.mega-nerd.com/SRC/
+[CoreAudio]: https://developer.apple.com/reference/coreaudio
+[Atlas]: http://math-atlas.sourceforge.net/
+[Blas]: https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms
+[fftw3]: http://fftw.org
+[Accelerate]: https://developer.apple.com/reference/accelerate
+[Intel IPP]: https://software.intel.com/en-us/intel-ipp
+
+[demos_dir]:https://github.com/aubio/aubio/tree/master/python/demos
+[pyaudio]:https://people.csail.mit.edu/hubert/pyaudio/
+[PySoundCard]:https://github.com/bastibe/PySoundCard
+[pyalsaaudio]:https://larsimmisch.github.io/pyalsaaudio/
+[mido]:https://mido.readthedocs.io
+
+[manual]: https://aubio.org/manual/latest/
+[doc_python]: https://aubio.org/manual/latest/python.html
+[doc_python_install]: https://aubio.org/manual/latest/python_module.html
+[homepage]: https://aubio.org
+[NumPy]: https://www.numpy.org
+[bugtracker]: https://github.com/aubio/aubio/issues
+[matplotlib]:https://matplotlib.org/
elif params.mode in ['default']:
pass
else:
- print("unknown mode {:s}".format(params.mode))
+ raise ValueError("unknown mode {:s}".format(params.mode))
# manual settings
if 'samplerate' in params:
samplerate = params.samplerate
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--mode',
help="mode [default|fast|super-fast]",
- dest="mode")
+ dest="mode", default='default')
parser.add_argument('sources',
- nargs='*',
+ nargs='+',
help="input_files")
args = parser.parse_args()
for f in args.sources:
#! /usr/bin/env python
+import sys
+import os.path
+import aubio
-def apply_filter(path):
- from aubio import source, sink, digital_filter
- from os.path import basename, splitext
+def apply_filter(path, target):
# open input file, get its samplerate
- s = source(path)
+ s = aubio.source(path)
samplerate = s.samplerate
# create an A-weighting filter
- f = digital_filter(7)
+ f = aubio.digital_filter(7)
f.set_a_weighting(samplerate)
- # alternatively, apply another filter
# create output file
- o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate)
+ o = aubio.sink(target, samplerate)
total_frames = 0
while True:
+ # read from source
samples, read = s()
+ # filter samples
filtered_samples = f(samples)
+ # write to sink
o(filtered_samples, read)
+ # count frames read
total_frames += read
- if read < s.hop_size: break
+ # end of file reached
+ if read < s.hop_size:
+ break
+ # print some info
duration = total_frames / float(samplerate)
- print ("read {:s}".format(s.uri))
- print ("applied A-weighting filtered ({:d} Hz)".format(samplerate))
- print ("wrote {:s} ({:.2f} s)".format(o.uri, duration))
+ input_str = "input: {:s} ({:.2f} s, {:d} Hz)"
+ output_str = "output: {:s}, A-weighting filtered ({:d} frames total)"
+ print(input_str.format(s.uri, duration, samplerate))
+ print(output_str.format(o.uri, total_frames))
if __name__ == '__main__':
- import sys
- for f in sys.argv[1:]:
- apply_filter(f)
+ usage = "{:s} <input_file> [output_file]".format(sys.argv[0])
+ if not 1 < len(sys.argv) < 4:
+ print(usage)
+ sys.exit(1)
+ if len(sys.argv) < 3:
+ input_path = sys.argv[1]
+ basename = os.path.splitext(os.path.basename(input_path))[0] + ".wav"
+ output_path = "filtered_" + basename
+ else:
+ input_path, output_path = sys.argv[1:]
+ # run function
+ apply_filter(input_path, output_path)
#! /usr/bin/env python
-from aubio import filterbank, fvec
-from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title
-from numpy import vstack, arange
+"""Create a filterbank from a list of frequencies.
-win_s = 2048
+This demo uses `aubio.filterbank.set_triangle_bands` to build a set of
+triangular filters from a list of frequencies.
+
+The filterbank coefficients are then modified before being displayed."""
+
+import aubio
+import numpy as np
+import matplotlib.pyplot as plt
+
+# sampling rate and size of the fft
samplerate = 48000
+win_s = 2048
+# define a list of custom frequency
freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]
+# number of filters to create
n_filters = len(freq_list) - 2
-f = filterbank(n_filters, win_s)
-freqs = fvec(freq_list)
+# create a new filterbank
+f = aubio.filterbank(n_filters, win_s)
+freqs = aubio.fvec(freq_list)
f.set_triangle_bands(freqs, samplerate)
+# get the coefficients from the filterbank
coeffs = f.get_coeffs()
-coeffs[4] *= 5.
-
+# apply a gain to fifth band
+coeffs[4] *= 6.
+# load the modified coeffs into the filterbank
f.set_coeffs(coeffs)
-times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
-title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.')
-loglog(times.T, f.get_coeffs().T, '.-')
-xlim([50, samplerate/2])
-ylim([1.0e-6, 2.0e-2])
-xlabel('log frequency (Hz)')
-ylabel('log amplitude')
-
-show()
+# display the band gains in a loglog plot
+freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
+plt.title('filterbank built from a list of frequencies\n'
+ 'The 5th band has been amplified by a factor 6.')
+plt.loglog(freqs.T, f.get_coeffs().T, '.-')
+plt.xlim([50, samplerate/2])
+plt.ylim([1.0e-6, 2.0e-2])
+plt.xlabel('log frequency (Hz)')
+plt.ylabel('log amplitude')
+plt.show()
pointer += partition
pointer += partition
-freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length/8)
+freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length//8)
a = build_sinusoid(sin_length, freqs, samplerate)
#! /usr/bin/env python
-import sys, aubio
+
+"""A simple example using aubio.source."""
+
+import sys
+import aubio
samplerate = 0 # use original source samplerate
-hop_size = 256 # number of frames to read in one block
-s = aubio.source(sys.argv[1], samplerate, hop_size)
+hop_size = 256 # number of frames to read in one block
+src = aubio.source(sys.argv[1], samplerate, hop_size)
total_frames = 0
-while True: # reading loop
- samples, read = s()
- total_frames += read
- if read < hop_size: break # end of file reached
+while True:
+ samples, read = src() # read hop_size new samples from source
+ total_frames += read # increment total number of frames
+ if read < hop_size: # end of file reached
+ break
fmt_string = "read {:d} frames at {:d}Hz from {:s}"
-print (fmt_string.format(total_frames, s.samplerate, sys.argv[1]))
-
+print(fmt_string.format(total_frames, src.samplerate, src.uri))
delta = frames2tick(total_frames) - last_time
if new_note[2] > 0:
track.append(Message('note_off', note=int(new_note[2]),
- velocity=127, time=0)
+ velocity=127, time=delta)
)
track.append(Message('note_on',
note=int(new_note[0]),
--- /dev/null
+#define PYAUBIO_dct_doc \
+ "dct(size=1024)\n"\
+ "\n"\
+ "Compute Discrete Fourier Transorms of Type-II.\n"\
+ "\n"\
+ "Parameters\n"\
+ "----------\n"\
+ "size : int\n"\
+ " size of the DCT to compute\n"\
+ "\n"\
+ "Example\n"\
+ "-------\n"\
+ ">>> d = aubio.dct(16)\n"\
+ ">>> d.size\n"\
+ "16\n"\
+ ">>> x = aubio.fvec(np.ones(d.size))\n"\
+ ">>> d(x)\n"\
+ "array([4., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n"\
+ " dtype=float32)\n"\
+ ">>> d.rdo(d(x))\n"\
+ "array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n"\
+ " dtype=float32)\n"\
+ "\n"\
+ "References\n"\
+ "----------\n"\
+ "`DCT-II in Discrete Cosine Transform\n"\
+ "<https://en.wikipedia.org/wiki/Discrete_cosine_transform#DCT-II>`_\n"\
+ "on Wikipedia.\n"
+
+#define PYAUBIO_mfcc_doc \
+ "mfcc(buf_size=1024, n_filters=40, n_coeffs=13, samplerate=44100)\n"\
+ "\n"\
+ "Compute Mel Frequency Cepstrum Coefficients (MFCC).\n"\
+ "\n"\
+ "`mfcc` creates a callable which takes a `cvec` as input.\n"\
+ "\n"\
+ "If `n_filters = 40`, the filterbank will be initialized with\n"\
+ ":meth:`filterbank.set_mel_coeffs_slaney`. Otherwise, if `n_filters`\n"\
+ "is greater than `0`, it will be initialized with\n"\
+ ":meth:`filterbank.set_mel_coeffs` using `fmin = 0`,\n"\
+ "`fmax = samplerate/`.\n"\
+ "\n"\
+ "Example\n"\
+ "-------\n"\
+ ">>> buf_size = 2048; n_filters = 128; n_coeffs = 13; samplerate = 44100\n"\
+ ">>> mf = aubio.mfcc(buf_size, n_filters, n_coeffs, samplerate)\n"\
+ ">>> fftgrain = aubio.cvec(buf_size)\n"\
+ ">>> mf(fftgrain).shape\n"\
+ "(13,)\n"\
+ ""
+
+#define PYAUBIO_notes_doc \
+ "notes(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+ "\n"\
+ "Note detection\n"
+
+#define PYAUBIO_onset_doc \
+ "onset(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+ "\n"\
+ "Onset detection object. `method` should be one of method supported by\n"\
+ ":class:`specdesc`.\n"
+
+#define PYAUBIO_pitch_doc \
+ "pitch(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+ "\n"\
+ "Pitch detection.\n"\
+ "\n"\
+ "Supported methods: `yinfft`, `yin`, `yinfast`, `fcomb`, `mcomb`,\n"\
+ "`schmitt`, `specacf`, `default` (`yinfft`).\n"
+
+#define PYAUBIO_sampler_doc \
+ "sampler(hop_size=512, samplerate=44100)\n"\
+ "\n"\
+ "Sampler.\n"
+
+#define PYAUBIO_specdesc_doc \
+ "specdesc(method=\"default\", buf_size=1024)\n"\
+ "\n"\
+ "Spectral description functions. Creates a callable that takes a\n"\
+ ":class:`cvec` as input, typically created by :class:`pvoc` for\n"\
+ "overlap and windowing, and returns a single float.\n"\
+ "\n"\
+ "`method` can be any of the values listed below. If `default` is used\n"\
+ "the `hfc` function will be selected.\n"\
+ "\n"\
+ "Onset novelty functions:\n"\
+ "\n"\
+ "- `energy`: local energy,\n"\
+ "- `hfc`: high frequency content,\n"\
+ "- `complex`: complex domain,\n"\
+ "- `phase`: phase-based method,\n"\
+ "- `wphase`: weighted phase deviation,\n"\
+ "- `specdiff`: spectral difference,\n"\
+ "- `kl`: Kullback-Liebler,\n"\
+ "- `mkl`: modified Kullback-Liebler,\n"\
+ "- `specflux`: spectral flux.\n"\
+ "\n"\
+ "Spectral shape functions:\n"\
+ "\n"\
+ "- `centroid`: spectral centroid (barycenter of the norm vector),\n"\
+ "- `spread`: variance around centroid,\n"\
+ "- `skewness`: third order moment,\n"\
+ "- `kurtosis`: a measure of the flatness of the spectrum,\n"\
+ "- `slope`: decreasing rate of the amplitude,\n"\
+ "- `decrease`: perceptual based measurement of the decreasing rate,\n"\
+ "- `rolloff`: 95th energy percentile.\n"\
+ "\n"\
+ "Parameters\n"\
+ "----------\n"\
+ "method : str\n"\
+ " Onset novelty or spectral shape function.\n"\
+ "buf_size : int\n"\
+ " Length of the input frame.\n"\
+ "\n"\
+ "Example\n"\
+ "-------\n"\
+ ">>> win_s = 1024; hop_s = win_s // 2\n"\
+ ">>> pv = aubio.pvoc(win_s, hop_s)\n"\
+ ">>> sd = aubio.specdesc(\"mkl\", win_s)\n"\
+ ">>> sd(pv(aubio.fvec(hop_s))).shape\n"\
+ "(1,)\n"\
+ "\n"\
+ "References\n"\
+ "----------\n"\
+ "`Detailed description "\
+ "<https://aubio.org/doc/latest/specdesc_8h.html#details>`_ in\n"\
+ "`aubio API documentation <https://aubio.org/doc/latest/index.html>`_.\n"\
+ ""
+
+#define PYAUBIO_tempo_doc \
+ "tempo(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+ "\n"\
+ "Tempo detection and beat tracking.\n"
+
+#define PYAUBIO_tss_doc \
+ "tss(buf_size=1024, hop_size=512)\n"\
+ "\n"\
+ "Transient/Steady-state separation.\n"
+
+#define PYAUBIO_wavetable_doc \
+ "wavetable(samplerate=44100, hop_size=512)\n"\
+ "\n"\
+ "Wavetable synthesis.\n"
#include <Python.h>
#include <structmember.h>
+#include "aubio-docstrings.h"
#include "aubio-generated.h"
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
static char aubio_module_doc[] = "Python module for the aubio library";
static char Py_alpha_norm_doc[] = ""
-"alpha_norm(fvec, integer) -> float\n"
+"alpha_norm(vec, alpha)\n"
"\n"
-"Compute alpha normalisation factor on vector, given alpha\n"
+"Compute `alpha` normalisation factor of vector `vec`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector\n"
+"alpha : float\n"
+" norm factor\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" p-norm of the input vector, where `p=alpha`\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> b = alpha_norm(a, 9)";
+">>> a = aubio.fvec(np.arange(10)); alpha = 2\n"
+">>> aubio.alpha_norm(a, alpha), (sum(a**alpha)/len(a))**(1./alpha)\n"
+"(5.338539123535156, 5.338539126015656)\n"
+"\n"
+"Note\n"
+"----\n"
+"Computed as:\n"
+"\n"
+".. math::\n"
+" l_{\\alpha} = \n"
+" \\|\\frac{\\sum_{n=0}^{N-1}{{x_n}^{\\alpha}}}{N}\\|^{1/\\alpha}\n"
+"";
static char Py_bintomidi_doc[] = ""
-"bintomidi(float, samplerate = integer, fftsize = integer) -> float\n"
+"bintomidi(fftbin, samplerate, fftsize)\n"
+"\n"
+"Convert FFT bin to frequency in midi note, given the sampling rate\n"
+"and the size of the FFT.\n"
"\n"
-"Convert bin (float) to midi (float), given the sampling rate and the FFT size\n"
+"Parameters\n"
+"----------\n"
+"fftbin : float\n"
+" input frequency bin\n"
+"samplerate : float\n"
+" sampling rate of the signal\n"
+"fftsize : float\n"
+" size of the FFT\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" Frequency converted to midi note.\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> midi = bintomidi(float, samplerate = 44100, fftsize = 1024)";
+">>> aubio.bintomidi(10, 44100, 1024)\n"
+"68.62871551513672\n"
+"";
static char Py_miditobin_doc[] = ""
-"miditobin(float, samplerate = integer, fftsize = integer) -> float\n"
+"miditobin(midi, samplerate, fftsize)\n"
"\n"
-"Convert midi (float) to bin (float), given the sampling rate and the FFT size\n"
+"Convert frequency in midi note to FFT bin, given the sampling rate\n"
+"and the size of the FFT.\n"
"\n"
-"Example\n"
+"Parameters\n"
+"----------\n"
+"midi : float\n"
+" input frequency, in midi note\n"
+"samplerate : float\n"
+" sampling rate of the signal\n"
+"fftsize : float\n"
+" size of the FFT\n"
+"\n"
+"Returns\n"
"-------\n"
+"float\n"
+" Frequency converted to FFT bin.\n"
+"\n"
+"Examples\n"
+"--------\n"
"\n"
-">>> bin = miditobin(midi, samplerate = 44100, fftsize = 1024)";
+">>> aubio.miditobin(69, 44100, 1024)\n"
+"10.216779708862305\n"
+">>> aubio.miditobin(75.08, 32000, 512)\n"
+"10.002175331115723\n"
+"";
static char Py_bintofreq_doc[] = ""
-"bintofreq(float, samplerate = integer, fftsize = integer) -> float\n"
+"bintofreq(fftbin, samplerate, fftsize)\n"
+"\n"
+"Convert FFT bin to frequency in Hz, given the sampling rate\n"
+"and the size of the FFT.\n"
"\n"
-"Convert bin number (float) in frequency (Hz), given the sampling rate and the FFT size\n"
+"Parameters\n"
+"----------\n"
+"fftbin : float\n"
+" input frequency bin\n"
+"samplerate : float\n"
+" sampling rate of the signal\n"
+"fftsize : float\n"
+" size of the FFT\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" Frequency converted to Hz.\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> freq = bintofreq(bin, samplerate = 44100, fftsize = 1024)";
+">>> aubio.bintofreq(10, 44100, 1024)\n"
+"430.6640625\n"
+"";
static char Py_freqtobin_doc[] = ""
-"freqtobin(float, samplerate = integer, fftsize = integer) -> float\n"
+"freqtobin(freq, samplerate, fftsize)\n"
"\n"
-"Convert frequency (Hz) in bin number (float), given the sampling rate and the FFT size\n"
+"Convert frequency in Hz to FFT bin, given the sampling rate\n"
+"and the size of the FFT.\n"
"\n"
-"Example\n"
+"Parameters\n"
+"----------\n"
+"midi : float\n"
+" input frequency, in midi note\n"
+"samplerate : float\n"
+" sampling rate of the signal\n"
+"fftsize : float\n"
+" size of the FFT\n"
+"\n"
+"Returns\n"
"-------\n"
+"float\n"
+" Frequency converted to FFT bin.\n"
+"\n"
+"Examples\n"
+"--------\n"
"\n"
-">>> bin = freqtobin(freq, samplerate = 44100, fftsize = 1024)";
+">>> aubio.freqtobin(440, 44100, 1024)\n"
+"10.216779708862305\n"
+"";
static char Py_zero_crossing_rate_doc[] = ""
-"zero_crossing_rate(fvec) -> float\n"
+"zero_crossing_rate(vec)\n"
+"\n"
+"Compute zero-crossing rate of `vec`.\n"
"\n"
-"Compute Zero crossing rate of a vector\n"
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" Zero-crossing rate.\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> z = zero_crossing_rate(a)";
+">>> a = np.linspace(-1., 1., 1000, dtype=aubio.float_type)\n"
+">>> aubio.zero_crossing_rate(a), 1/1000\n"
+"(0.0010000000474974513, 0.001)\n"
+"";
static char Py_min_removal_doc[] = ""
-"min_removal(fvec) -> float\n"
+"min_removal(vec)\n"
+"\n"
+"Remove the minimum value of a vector to each of its element.\n"
"\n"
-"Remove the minimum value of a vector, in-place modification\n"
+"Modifies the input vector in-place and returns a reference to it.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector\n"
+"\n"
+"Returns\n"
+"-------\n"
+"fvec\n"
+" modified input vector\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> min_removal(a)";
+">>> aubio.min_removal(aubio.fvec(np.arange(1,4)))\n"
+"array([0., 1., 2.], dtype=" AUBIO_NPY_SMPL_STR ")\n"
+"";
extern void add_ufuncs ( PyObject *m );
extern int generated_types_ready(void);
}
// compute the function
- result = Py_BuildValue (AUBIO_NPY_SMPL_CHR, fvec_alpha_norm (&vec, alpha));
+ result = PyFloat_FromDouble(fvec_alpha_norm (&vec, alpha));
if (result == NULL) {
return NULL;
}
smpl_t input, samplerate, fftsize;
smpl_t output;
- if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) {
+ if (!PyArg_ParseTuple (args,
+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
+ &input, &samplerate, &fftsize)) {
return NULL;
}
smpl_t input, samplerate, fftsize;
smpl_t output;
- if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) {
+ if (!PyArg_ParseTuple (args,
+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
+ &input, &samplerate, &fftsize)) {
return NULL;
}
smpl_t input, samplerate, fftsize;
smpl_t output;
- if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) {
+ if (!PyArg_ParseTuple (args,
+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
+ &input, &samplerate, &fftsize)) {
return NULL;
}
smpl_t input, samplerate, fftsize;
smpl_t output;
- if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) {
+ if (!PyArg_ParseTuple (args,
+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
+ &input, &samplerate, &fftsize)) {
return NULL;
}
}
// compute the function
- result = Py_BuildValue (AUBIO_NPY_SMPL_CHR, aubio_zero_crossing_rate (&vec));
+ result = PyFloat_FromDouble(aubio_zero_crossing_rate (&vec));
if (result == NULL) {
return NULL;
}
{"silence_detection", Py_aubio_silence_detection, METH_VARARGS, Py_aubio_silence_detection_doc},
{"level_detection", Py_aubio_level_detection, METH_VARARGS, Py_aubio_level_detection_doc},
{"window", Py_aubio_window, METH_VARARGS, Py_aubio_window_doc},
+ {"shift", Py_aubio_shift, METH_VARARGS, Py_aubio_shift_doc},
+ {"ishift", Py_aubio_ishift, METH_VARARGS, Py_aubio_ishift_doc},
+ {"hztomel", Py_aubio_hztomel, METH_VARARGS|METH_KEYWORDS, Py_aubio_hztomel_doc},
+ {"meltohz", Py_aubio_meltohz, METH_VARARGS|METH_KEYWORDS, Py_aubio_meltohz_doc},
+ {"hztomel_htk", Py_aubio_hztomel_htk, METH_VARARGS, Py_aubio_hztomel_htk_doc},
+ {"meltohz_htk", Py_aubio_meltohz_htk, METH_VARARGS, Py_aubio_meltohz_htk_doc},
{NULL, NULL, 0, NULL} /* Sentinel */
};
uint_t length;
} Py_cvec;
-static char Py_cvec_doc[] = "cvec object";
+static char Py_cvec_doc[] = ""
+"cvec(size)\n"
+"\n"
+"A container holding spectral data.\n"
+"\n"
+"Create one `cvec` to store the spectral information of a window\n"
+"of `size` points. The data will be stored in two vectors,\n"
+":attr:`phas` and :attr:`norm`, each of shape (:attr:`length`,),\n"
+"with `length = size // 2 + 1`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"size: int\n"
+" Size of spectrum to create.\n"
+"\n"
+"Examples\n"
+"--------\n"
+">>> c = aubio.cvec(1024)\n"
+">>> c\n"
+"aubio cvec of 513 elements\n"
+">>> c.length\n"
+"513\n"
+">>> c.norm.dtype, c.phas.dtype\n"
+"(dtype('float32'), dtype('float32'))\n"
+">>> c.norm.shape, c.phas.shape\n"
+"((513,), (513,))\n"
+"\n"
+"See Also\n"
+"--------\n"
+"fvec, fft, pvoc\n"
+"";
PyObject *
goto fail;
}
- args = Py_BuildValue ("I", self->length);
+ args = PyLong_FromLong(self->length);
if (args == NULL) {
goto fail;
}
static PyMemberDef Py_cvec_members[] = {
// TODO remove READONLY flag and define getter/setter
{"length", T_INT, offsetof (Py_cvec, length), READONLY,
- "length attribute"},
+ "int: Length of `norm` and `phas` vectors."},
{NULL} /* Sentinel */
};
};
static PyGetSetDef Py_cvec_getseters[] = {
- {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm,
- "Numpy vector of shape (length,) containing the magnitude",
+ {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm,
+ "numpy.ndarray: Vector of shape `(length,)` containing the magnitude.",
NULL},
- {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas,
- "Numpy vector of shape (length,) containing the phase",
+ {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas,
+ "numpy.ndarray: Vector of shape `(length,)` containing the phase.",
NULL},
{NULL} /* sentinel */
};
#include "aubio-types.h"
-static char Py_fft_doc[] = "fft object";
+static char Py_fft_doc[] = ""
+"fft(size=1024)\n"
+"\n"
+"Compute Fast Fourier Transorms.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"size : int\n"
+" size of the FFT to compute\n"
+"\n"
+"Example\n"
+"-------\n"
+">>> x = aubio.fvec(512)\n"
+">>> f = aubio.fft(512)\n"
+">>> c = f(x); c\n"
+"aubio cvec of 257 elements\n"
+">>> x2 = f.rdo(c); x2.shape\n"
+"(512,)\n"
+"";
typedef struct
{
fvec_t c_out;
} Py_filter;
-static char Py_filter_doc[] = "filter object";
+static char Py_filter_doc[] = ""
+"digital_filter(order=7)\n"
+"\n"
+"Create a digital filter.\n"
+"";
+
+static char Py_filter_set_c_weighting_doc[] = ""
+"set_c_weighting(samplerate)\n"
+"\n"
+"Set filter coefficients to C-weighting.\n"
+"\n"
+"`samplerate` should be one of 8000, 11025, 16000, 22050, 24000, 32000,\n"
+"44100, 48000, 88200, 96000, or 192000. `order` of the filter should be 5.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : int\n"
+" Sampling-rate of the input signal, in Hz.\n"
+"";
+
+static char Py_filter_set_a_weighting_doc[] = ""
+"set_a_weighting(samplerate)\n"
+"\n"
+"Set filter coefficients to A-weighting.\n"
+"\n"
+"`samplerate` should be one of 8000, 11025, 16000, 22050, 24000, 32000,\n"
+"44100, 48000, 88200, 96000, or 192000. `order` of the filter should be 7.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : int\n"
+" Sampling-rate of the input signal.\n"
+"";
+
+static char Py_filter_set_biquad_doc[] = ""
+"set_biquad(b0, b1, b2, a1, a2)\n"
+"\n"
+"Set biquad coefficients. `order` of the filter should be 3.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"b0 : float\n"
+" Forward filter coefficient.\n"
+"b1 : float\n"
+" Forward filter coefficient.\n"
+"b2 : float\n"
+" Forward filter coefficient.\n"
+"a1 : float\n"
+" Feedback filter coefficient.\n"
+"a2 : float\n"
+" Feedback filter coefficient.\n"
+"";
static PyObject *
Py_filter_new (PyTypeObject * type, PyObject * args, PyObject * kwds)
Py_filter_del (Py_filter * self)
{
Py_XDECREF(self->out);
- del_aubio_filter (self->o);
+ if (self->o)
+ del_aubio_filter (self->o);
Py_TYPE(self)->tp_free ((PyObject *) self);
}
static PyMethodDef Py_filter_methods[] = {
{"set_c_weighting", (PyCFunction) Py_filter_set_c_weighting, METH_VARARGS,
- "set filter coefficients to C-weighting"},
+ Py_filter_set_c_weighting_doc},
{"set_a_weighting", (PyCFunction) Py_filter_set_a_weighting, METH_VARARGS,
- "set filter coefficients to A-weighting"},
+ Py_filter_set_a_weighting_doc},
{"set_biquad", (PyCFunction) Py_filter_set_biquad, METH_VARARGS,
- "set b0, b1, b2, a1, a2 biquad coefficients"},
+ Py_filter_set_biquad_doc},
{NULL}
};
#include "aubio-types.h"
-static char Py_filterbank_doc[] = "filterbank object";
+static char Py_filterbank_doc[] = ""
+"filterbank(n_filters=40, win_s=1024)\n"
+"\n"
+"Create a bank of spectral filters. Each instance is a callable\n"
+"that holds a matrix of coefficients.\n"
+"\n"
+"See also :meth:`set_mel_coeffs`, :meth:`set_mel_coeffs_htk`,\n"
+":meth:`set_mel_coeffs_slaney`, :meth:`set_triangle_bands`, and\n"
+":meth:`set_coeffs`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"n_filters : int\n"
+" Number of filters to create.\n"
+"win_s : int\n"
+" Size of the input spectrum to process.\n"
+"\n"
+"Examples\n"
+"--------\n"
+">>> f = aubio.filterbank(128, 1024)\n"
+">>> f.set_mel_coeffs(44100, 0, 10000)\n"
+">>> c = aubio.cvec(1024)\n"
+">>> f(c).shape\n"
+"(128, )\n"
+"";
+
+static char Py_filterbank_set_triangle_bands_doc[] =""
+"set_triangle_bands(freqs, samplerate)\n"
+"\n"
+"Set triangular bands. The coefficients will be set to triangular\n"
+"overlapping windows using the boundaries specified by `freqs`.\n"
+"\n"
+"`freqs` should contain `n_filters + 2` frequencies in Hz, ordered\n"
+"by value, from smallest to largest. The first element should be greater\n"
+"or equal to zero; the last element should be smaller or equal to\n"
+"`samplerate / 2`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"freqs: fvec\n"
+" List of frequencies, in Hz.\n"
+"samplerate : float\n"
+" Sampling-rate of the expected input.\n"
+"\n"
+"Example\n"
+"-------\n"
+">>> fb = aubio.filterbank(n_filters=100, win_s=2048)\n"
+">>> samplerate = 44100; freqs = np.linspace(0, 20200, 102)\n"
+">>> fb.set_triangle_bands(aubio.fvec(freqs), samplerate)\n"
+"";
+
+static char Py_filterbank_set_mel_coeffs_slaney_doc[] = ""
+"set_mel_coeffs_slaney(samplerate)\n"
+"\n"
+"Set coefficients of filterbank to match Slaney's Auditory Toolbox.\n"
+"\n"
+"The filter coefficients will be set as in Malcolm Slaney's\n"
+"implementation. The filterbank should have been created with\n"
+"`n_filters = 40`.\n"
+"\n"
+"This is approximately equivalent to using :meth:`set_mel_coeffs` with\n"
+"`fmin = 400./3., fmax = 6853.84`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : float\n"
+" Sampling-rate of the expected input.\n"
+"\n"
+"References\n"
+"----------\n"
+"\n"
+"Malcolm Slaney, `Auditory Toolbox Version 2, Technical Report #1998-010\n"
+"<https://engineering.purdue.edu/~malcolm/interval/1998-010/>`_\n"
+"";
+
+static char Py_filterbank_set_mel_coeffs_doc[] = ""
+"set_mel_coeffs(samplerate, fmin, fmax)\n"
+"\n"
+"Set coefficients of filterbank to linearly spaced mel scale.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : float\n"
+" Sampling-rate of the expected input.\n"
+"fmin : float\n"
+" Lower frequency boundary of the first filter.\n"
+"fmax : float\n"
+" Upper frequency boundary of the last filter.\n"
+"\n"
+"See also\n"
+"--------\n"
+"hztomel\n"
+"";
+
+static char Py_filterbank_set_mel_coeffs_htk_doc[] = ""
+"set_mel_coeffs_htk(samplerate, fmin, fmax)\n"
+"\n"
+"Set coefficients of the filters to be linearly spaced in the HTK mel scale.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : float\n"
+" Sampling-rate of the expected input.\n"
+"fmin : float\n"
+" Lower frequency boundary of the first filter.\n"
+"fmax : float\n"
+" Upper frequency boundary of the last filter.\n"
+"\n"
+"See also\n"
+"--------\n"
+"hztomel with `htk=True`\n"
+"";
+
+static char Py_filterbank_get_coeffs_doc[] = ""
+"get_coeffs()\n"
+"\n"
+"Get coefficients matrix of filterbank.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"array_like\n"
+" Array of shape (n_filters, win_s/2+1) containing the coefficients.\n"
+"";
+
+static char Py_filterbank_set_coeffs_doc[] = ""
+"set_coeffs(coeffs)\n"
+"\n"
+"Set coefficients of filterbank.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"coeffs : fmat\n"
+" Array of shape (n_filters, win_s/2+1) containing the coefficients.\n"
+"";
+
+static char Py_filterbank_set_power_doc[] = ""
+"set_power(power)\n"
+"\n"
+"Set power applied to input spectrum of filterbank.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"power : float\n"
+" Power to raise input spectrum to before computing the filters.\n"
+"";
+
+static char Py_filterbank_get_power_doc[] = ""
+"get_power()\n"
+"\n"
+"Get power applied to filterbank.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" Power parameter.\n"
+"";
+
+static char Py_filterbank_set_norm_doc[] = ""
+"set_norm(norm)\n"
+"\n"
+"Set norm parameter. If set to `0`, the filters will not be normalized.\n"
+"If set to `1`, the filters will be normalized to one. Default to `1`.\n"
+"\n"
+"This function should be called *before* :meth:`set_triangle_bands`,\n"
+":meth:`set_mel_coeffs`, :meth:`set_mel_coeffs_htk`, or\n"
+":meth:`set_mel_coeffs_slaney`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"norm : int\n"
+" `0` to disable, `1` to enable\n"
+"";
+
+static char Py_filterbank_get_norm_doc[] = ""
+"get_norm()\n"
+"\n"
+"Get norm parameter of filterbank.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" Norm parameter.\n"
+"";
typedef struct
{
if (self->vec.length != self->win_s / 2 + 1) {
PyErr_Format(PyExc_ValueError,
- "input cvec has length %d, but fft expects length %d",
+ "input cvec has length %d, but filterbank expects length %d",
self->vec.length, self->win_s / 2 + 1);
return NULL;
}
uint_t err = 0;
PyObject *input;
- uint_t samplerate;
- if (!PyArg_ParseTuple (args, "OI", &input, &samplerate)) {
+ smpl_t samplerate;
+ if (!PyArg_ParseTuple (args, "O" AUBIO_NPY_SMPL_CHR, &input, &samplerate)) {
return NULL;
}
err = aubio_filterbank_set_triangle_bands (self->o,
&(self->freqs), samplerate);
if (err > 0) {
- PyErr_SetString (PyExc_ValueError,
- "error when setting filter to A-weighting");
+ if (PyErr_Occurred() == NULL) {
+ PyErr_SetString (PyExc_ValueError, "error running set_triangle_bands");
+ } else {
+ // change the RuntimeError into ValueError
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_Restore(PyExc_ValueError, value, traceback);
+ }
return NULL;
}
Py_RETURN_NONE;
{
uint_t err = 0;
- uint_t samplerate;
- if (!PyArg_ParseTuple (args, "I", &samplerate)) {
+ smpl_t samplerate;
+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &samplerate)) {
return NULL;
}
err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate);
if (err > 0) {
- PyErr_SetString (PyExc_ValueError,
- "error when setting filter to A-weighting");
+ if (PyErr_Occurred() == NULL) {
+ PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_slaney");
+ } else {
+ // change the RuntimeError into ValueError
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_Restore(PyExc_ValueError, value, traceback);
+ }
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+Py_filterbank_set_mel_coeffs (Py_filterbank * self, PyObject *args)
+{
+ uint_t err = 0;
+
+ smpl_t samplerate;
+ smpl_t freq_min;
+ smpl_t freq_max;
+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR
+ AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) {
+ return NULL;
+ }
+
+ err = aubio_filterbank_set_mel_coeffs (self->o, samplerate,
+ freq_min, freq_max);
+ if (err > 0) {
+ if (PyErr_Occurred() == NULL) {
+ PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs");
+ } else {
+ // change the RuntimeError into ValueError
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_Restore(PyExc_ValueError, value, traceback);
+ }
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+Py_filterbank_set_mel_coeffs_htk (Py_filterbank * self, PyObject *args)
+{
+ uint_t err = 0;
+
+ smpl_t samplerate;
+ smpl_t freq_min;
+ smpl_t freq_max;
+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR
+ AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) {
+ return NULL;
+ }
+
+ err = aubio_filterbank_set_mel_coeffs_htk (self->o, samplerate,
+ freq_min, freq_max);
+ if (err > 0) {
+ if (PyErr_Occurred() == NULL) {
+ PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_htk");
+ } else {
+ // change the RuntimeError into ValueError
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_Restore(PyExc_ValueError, value, traceback);
+ }
return NULL;
}
Py_RETURN_NONE;
aubio_filterbank_get_coeffs (self->o) );
}
+static PyObject *
+Py_filterbank_set_power(Py_filterbank *self, PyObject *args)
+{
+ smpl_t power;
+
+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &power)) {
+ return NULL;
+ }
+ if(aubio_filterbank_set_power (self->o, power)) {
+ if (PyErr_Occurred() == NULL) {
+ PyErr_SetString (PyExc_ValueError,
+ "error running filterbank.set_power");
+ } else {
+ // change the RuntimeError into ValueError
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_Restore(PyExc_ValueError, value, traceback);
+ }
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+Py_filterbank_get_power (Py_filterbank * self, PyObject *unused)
+{
+ smpl_t power = aubio_filterbank_get_power(self->o);
+ return (PyObject *)PyFloat_FromDouble (power);
+}
+
+static PyObject *
+Py_filterbank_set_norm(Py_filterbank *self, PyObject *args)
+{
+ smpl_t norm;
+
+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &norm)) {
+ return NULL;
+ }
+ if(aubio_filterbank_set_norm (self->o, norm)) {
+ if (PyErr_Occurred() == NULL) {
+ PyErr_SetString (PyExc_ValueError,
+ "error running filterbank.set_power");
+ } else {
+ // change the RuntimeError into ValueError
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_Restore(PyExc_ValueError, value, traceback);
+ }
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+Py_filterbank_get_norm (Py_filterbank * self, PyObject *unused)
+{
+ smpl_t norm = aubio_filterbank_get_norm(self->o);
+ return (PyObject *)PyFloat_FromDouble (norm);
+}
+
static PyMethodDef Py_filterbank_methods[] = {
{"set_triangle_bands", (PyCFunction) Py_filterbank_set_triangle_bands,
- METH_VARARGS, "set coefficients of filterbanks"},
+ METH_VARARGS, Py_filterbank_set_triangle_bands_doc},
{"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney,
- METH_VARARGS, "set coefficients of filterbank as in Auditory Toolbox"},
+ METH_VARARGS, Py_filterbank_set_mel_coeffs_slaney_doc},
+ {"set_mel_coeffs", (PyCFunction) Py_filterbank_set_mel_coeffs,
+ METH_VARARGS, Py_filterbank_set_mel_coeffs_doc},
+ {"set_mel_coeffs_htk", (PyCFunction) Py_filterbank_set_mel_coeffs_htk,
+ METH_VARARGS, Py_filterbank_set_mel_coeffs_htk_doc},
{"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs,
- METH_NOARGS, "get coefficients of filterbank"},
+ METH_NOARGS, Py_filterbank_get_coeffs_doc},
{"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs,
- METH_VARARGS, "set coefficients of filterbank"},
+ METH_VARARGS, Py_filterbank_set_coeffs_doc},
+ {"set_power", (PyCFunction) Py_filterbank_set_power,
+ METH_VARARGS, Py_filterbank_set_power_doc},
+ {"get_power", (PyCFunction) Py_filterbank_get_power,
+ METH_NOARGS, Py_filterbank_get_power_doc},
+ {"set_norm", (PyCFunction) Py_filterbank_set_norm,
+ METH_VARARGS, Py_filterbank_set_norm_doc},
+ {"get_norm", (PyCFunction) Py_filterbank_get_norm,
+ METH_NOARGS, Py_filterbank_get_norm_doc},
{NULL}
};
return NULL;
}
- level_lin = Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_level_lin(&vec));
+ level_lin = PyFloat_FromDouble(aubio_level_lin(&vec));
if (level_lin == NULL) {
PyErr_SetString (PyExc_ValueError, "failed computing level_lin");
return NULL;
return NULL;
}
- db_spl = Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_db_spl(&vec));
+ db_spl = PyFloat_FromDouble(aubio_db_spl(&vec));
if (db_spl == NULL) {
PyErr_SetString (PyExc_ValueError, "failed computing db_spl");
return NULL;
return NULL;
}
- silence_detection = Py_BuildValue("I", aubio_silence_detection(&vec, threshold));
+ silence_detection = PyLong_FromLong(aubio_silence_detection(&vec, threshold));
if (silence_detection == NULL) {
PyErr_SetString (PyExc_ValueError, "failed computing silence_detection");
return NULL;
return NULL;
}
- level_detection = Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_level_detection(&vec, threshold));
+ level_detection = PyFloat_FromDouble(aubio_level_detection(&vec, threshold));
if (level_detection == NULL) {
PyErr_SetString (PyExc_ValueError, "failed computing level_detection");
return NULL;
return level_detection;
}
+
+PyObject *
+Py_aubio_shift(PyObject *self, PyObject *args)
+{
+ PyObject *input;
+ fvec_t vec;
+
+ if (!PyArg_ParseTuple (args, "O:shift", &input)) {
+ return NULL;
+ }
+
+ if (input == NULL) {
+ return NULL;
+ }
+
+ if (!PyAubio_ArrayToCFvec(input, &vec)) {
+ return NULL;
+ }
+
+ fvec_shift(&vec);
+
+ //Py_RETURN_NONE;
+ return (PyObject *) PyAubio_CFvecToArray(&vec);
+}
+
+PyObject *
+Py_aubio_ishift(PyObject *self, PyObject *args)
+{
+ PyObject *input;
+ fvec_t vec;
+
+ if (!PyArg_ParseTuple (args, "O:shift", &input)) {
+ return NULL;
+ }
+
+ if (input == NULL) {
+ return NULL;
+ }
+
+ if (!PyAubio_ArrayToCFvec(input, &vec)) {
+ return NULL;
+ }
+
+ fvec_ishift(&vec);
+
+ //Py_RETURN_NONE;
+ return (PyObject *) PyAubio_CFvecToArray(&vec);
+}
+
+PyObject*
+Py_aubio_hztomel(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ smpl_t v;
+ PyObject *htk = NULL;
+ static char *kwlist[] = {"f", "htk", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O",
+ kwlist, &v, &htk))
+ {
+ return NULL;
+ }
+ if (htk != NULL && PyObject_IsTrue(htk) == 1)
+ return PyFloat_FromDouble(aubio_hztomel_htk(v));
+ else
+ return PyFloat_FromDouble(aubio_hztomel(v));
+}
+
+PyObject*
+Py_aubio_meltohz(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ smpl_t v;
+ PyObject *htk = NULL;
+ static char *kwlist[] = {"m", "htk", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O",
+ kwlist, &v, &htk))
+ {
+ return NULL;
+ }
+ if (htk != NULL && PyObject_IsTrue(htk) == 1)
+ return PyFloat_FromDouble(aubio_meltohz_htk(v));
+ else
+ return PyFloat_FromDouble(aubio_meltohz(v));
+}
+
+PyObject*
+Py_aubio_hztomel_htk(PyObject *self, PyObject *args)
+{
+ smpl_t v;
+ if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) {
+ return NULL;
+ }
+ return PyFloat_FromDouble(aubio_hztomel_htk(v));
+}
+
+PyObject*
+Py_aubio_meltohz_htk(PyObject *self, PyObject *args)
+{
+ smpl_t v;
+ if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) {
+ return NULL;
+ }
+ return PyFloat_FromDouble(aubio_meltohz_htk(v));
+}
#define PY_AUBIO_MUSICUTILS_H
static char Py_aubio_window_doc[] = ""
-"window(string, integer) -> fvec\n"
+"window(window_type, size)\n"
"\n"
-"Create a window\n"
+"Create a window of length `size`. `window_type` should be one\n"
+"of the following:\n"
"\n"
-"Example\n"
+"- `default` (same as `hanningz`).\n"
+"- `ones`\n"
+"- `rectangle`\n"
+"- `hamming`\n"
+"- `hanning`\n"
+"- `hanningz` [1]_\n"
+"- `blackman`\n"
+"- `blackman_harris`\n"
+"- `gaussian`\n"
+"- `welch`\n"
+"- `parzen`\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"window_type : str\n"
+" Type of window.\n"
+"size : int\n"
+" Length of window.\n"
+"\n"
+"Returns\n"
"-------\n"
+"fvec\n"
+" Array of shape `(length,)` containing the new window.\n"
+"\n"
+"See Also\n"
+"--------\n"
+"pvoc, fft\n"
+"\n"
+"Examples\n"
+"--------\n"
+"Compute a zero-phase Hann window on `1024` points:\n"
"\n"
-">>> window('hanningz', 1024)\n"
+">>> aubio.window('hanningz', 1024)\n"
"array([ 0.00000000e+00, 9.41753387e-06, 3.76403332e-05, ...,\n"
-" 8.46982002e-05, 3.76403332e-05, 9.41753387e-06], dtype=float32)";
+" 8.46982002e-05, 3.76403332e-05, 9.41753387e-06], dtype=float32)\n"
+"\n"
+"Plot different window types with `matplotlib <https://matplotlib.org/>`_:\n"
+"\n"
+">>> import matplotlib.pyplot as plt\n"
+">>> modes = ['default', 'ones', 'rectangle', 'hamming', 'hanning',\n"
+"... 'hanningz', 'blackman', 'blackman_harris', 'gaussian',\n"
+"... 'welch', 'parzen']; n = 2048\n"
+">>> for m in modes: plt.plot(aubio.window(m, n), label=m)\n"
+"...\n"
+">>> plt.legend(); plt.show()\n"
+"\n"
+"Note\n"
+"----\n"
+"The following examples contain the equivalent source code to compute\n"
+"each type of window with `NumPy <https://numpy.org>`_:\n"
+"\n"
+">>> n = 1024; x = np.arange(n, dtype=aubio.float_type)\n"
+">>> ones = np.ones(n).astype(aubio.float_type)\n"
+">>> rectangle = 0.5 * ones\n"
+">>> hanning = 0.5 - 0.5 * np.cos(2 * np.pi * x / n)\n"
+">>> hanningz = 0.5 * (1 - np.cos(2 * np.pi * x / n))\n"
+">>> hamming = 0.54 - 0.46 * np.cos(2.*np.pi * x / (n - 1))\n"
+">>> blackman = 0.42 \\\n"
+"... - 0.50 * np.cos(2 * np.pi * x / (n - 1)) \\\n"
+"... + 0.08 * np.cos(4 * np.pi * x / (n - 1))\n"
+">>> blackman_harris = 0.35875 \\\n"
+"... - 0.48829 * np.cos(2 * np.pi * x / (n - 1)) \\\n"
+"... + 0.14128 * np.cos(4 * np.pi * x / (n - 1)) \\\n"
+"... + 0.01168 * np.cos(6 * np.pi * x / (n - 1))\n"
+">>> gaussian = np.exp( - 0.5 * ((x - 0.5 * (n - 1)) \\\n"
+"... / (0.25 * (n - 1)) )**2 )\n"
+">>> welch = 1 - ((2 * x - n) / (n + 1))**2\n"
+">>> parzen = 1 - np.abs((2 * x - n) / (n + 1))\n"
+">>> default = hanningz\n"
+"References\n"
+"----------\n"
+#if 0
+"`Window function <https://en.wikipedia.org/wiki/Window_function>`_ on\n"
+"Wikipedia.\n"
+"\n"
+#endif
+".. [1] Amalia de Götzen, Nicolas Bernardini, and Daniel Arfib. Traditional\n"
+" (?) implementations of a phase vocoder: the tricks of the trade.\n"
+" In *Proceedings of the International Conference on Digital Audio\n"
+" Effects* (DAFx-00), pages 37–44, University of Verona, Italy, 2000.\n"
+" (`online version <"
+"https://www.cs.princeton.edu/courses/archive/spr09/cos325/Bernardini.pdf"
+">`_).\n"
+"";
PyObject * Py_aubio_window(PyObject *self, PyObject *args);
static char Py_aubio_level_lin_doc[] = ""
-"level_lin(fvec) -> fvec\n"
+"level_lin(x)\n"
"\n"
-"Compute sound level on a linear scale.\n"
+"Compute sound pressure level of `x`, on a linear scale.\n"
"\n"
-"This gives the average of the square amplitudes.\n"
+"Parameters\n"
+"----------\n"
+"x : fvec\n"
+" input vector\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" Linear level of `x`.\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> level_Lin(numpy.ones(1024))\n"
-"1.0";
+">>> aubio.level_lin(aubio.fvec(numpy.ones(1024)))\n"
+"1.0\n"
+"\n"
+"Note\n"
+"----\n"
+"Computed as the average of the squared amplitudes:\n"
+"\n"
+".. math:: L = \\frac {\\sum_{n=0}^{N-1} {x_n}^2} {N}\n"
+"\n"
+"See Also\n"
+"--------\n"
+"db_spl, silence_detection, level_detection\n"
+"";
PyObject * Py_aubio_level_lin(PyObject *self, PyObject *args);
static char Py_aubio_db_spl_doc[] = ""
-"Compute sound pressure level (SPL) in dB\n"
+"db_spl(x)\n"
"\n"
-"This quantity is often wrongly called 'loudness'.\n"
+"Compute Sound Pressure Level (SPL) of `x`, in dB.\n"
"\n"
-"This gives ten times the log10 of the average of the square amplitudes.\n"
+"Parameters\n"
+"----------\n"
+"x : fvec\n"
+" input vector\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" Level of `x`, in dB SPL.\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> db_spl(numpy.ones(1024))\n"
-"1.0";
+">>> aubio.db_spl(aubio.fvec(np.ones(1024)))\n"
+"1.0\n"
+">>> aubio.db_spl(0.7*aubio.fvec(np.ones(32)))\n"
+"-3.098040819168091\n"
+"\n"
+"Note\n"
+"----\n"
+"Computed as `log10` of :py:func:`level_lin`:\n"
+"\n"
+".. math::\n"
+"\n"
+" {SPL}_{dB} = log10{\\frac {\\sum_{n=0}^{N-1}{x_n}^2} {N}}\n"
+"\n"
+"This quantity is often incorrectly called 'loudness'.\n"
+"\n"
+"See Also\n"
+"--------\n"
+"level_lin, silence_detection, level_detection\n"
+"";
PyObject * Py_aubio_db_spl(PyObject *self, PyObject *args);
static char Py_aubio_silence_detection_doc[] = ""
-"Check if buffer level in dB SPL is under a given threshold\n"
+"silence_detection(vec, level)\n"
"\n"
-"Return 0 if level is under the given threshold, 1 otherwise.\n"
+"Check if level of `vec`, in dB SPL, is under a given threshold.\n"
"\n"
-"Example\n"
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector\n"
+"level : float\n"
+" level threshold, in dB SPL\n"
+"\n"
+"Returns\n"
"-------\n"
+"int\n"
+" `1` if level of `vec`, in dB SPL, is under `level`,\n"
+" `0` otherwise.\n"
"\n"
-">>> import numpy\n"""
-">>> silence_detection(numpy.ones(1024, dtype=\"float32\"), -80)\n"
-"0";
+"Examples\n"
+"--------\n"
+"\n"
+">>> aubio.silence_detection(aubio.fvec(32), -100.)\n"
+"1\n"
+">>> aubio.silence_detection(aubio.fvec(np.ones(32)), 0.)\n"
+"0\n"
+"\n"
+"See Also\n"
+"--------\n"
+"level_detection, db_spl, level_lin\n"
+"";
PyObject * Py_aubio_silence_detection(PyObject *self, PyObject *args);
static char Py_aubio_level_detection_doc[] = ""
-"Get buffer level in dB SPL if over a given threshold, 1. otherwise.\n"
+"level_detection(vec, level)\n"
+"\n"
+"Check if `vec` is above threshold `level`, in dB SPL.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector\n"
+"level : float\n"
+" level threshold, in dB SPL\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" `1.0` if level of `vec` in dB SPL is under `level`,\n"
+" `db_spl(vec)` otherwise.\n"
"\n"
"Example\n"
"-------\n"
"\n"
-">>> import numpy\n"""
-">>> level_detection(0.7*numpy.ones(1024, dtype=\"float32\"), -80)\n"
-"0";
+">>> aubio.level_detection(0.7*aubio.fvec(np.ones(1024)), -3.)\n"
+"1.0\n"
+">>> aubio.level_detection(0.7*aubio.fvec(np.ones(1024)), -4.)\n"
+"-3.0980708599090576\n"
+"\n"
+"See Also\n"
+"--------\n"
+"silence_detection, db_spl, level_lin\n"
+"";
PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args);
+static char Py_aubio_shift_doc[] = ""
+"shift(vec)\n"
+"\n"
+"Swap left and right partitions of a vector, in-place.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector to shift\n"
+"\n"
+"Returns\n"
+"-------\n"
+"fvec\n"
+" The swapped vector.\n"
+"\n"
+"Notes\n"
+"-----\n"
+"The input vector is also modified.\n"
+"\n"
+"For a vector of length N, the partition is split at index N - N//2.\n"
+"\n"
+"Example\n"
+"-------\n"
+"\n"
+">>> aubio.shift(aubio.fvec(np.arange(3)))\n"
+"array([2., 0., 1.], dtype=" AUBIO_NPY_SMPL_STR ")\n"
+"\n"
+"See Also\n"
+"--------\n"
+"ishift\n"
+"";
+PyObject * Py_aubio_shift(PyObject *self, PyObject *args);
+
+static char Py_aubio_ishift_doc[] = ""
+"ishift(vec)\n"
+"\n"
+"Swap right and left partitions of a vector, in-place.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector to shift\n"
+"\n"
+"Returns\n"
+"-------\n"
+"fvec\n"
+" The swapped vector.\n"
+"\n"
+"Notes\n"
+"-----\n"
+"The input vector is also modified.\n"
+"\n"
+"Unlike with :py:func:`shift`, the partition is split at index N//2.\n"
+"\n"
+"Example\n"
+"-------\n"
+"\n"
+">>> aubio.ishift(aubio.fvec(np.arange(3)))\n"
+"array([1., 2., 0.], dtype=" AUBIO_NPY_SMPL_STR ")\n"
+"\n"
+"See Also\n"
+"--------\n"
+"shift\n"
+"";
+PyObject * Py_aubio_ishift(PyObject *self, PyObject *args);
+
+static char Py_aubio_hztomel_doc[] = ""
+"hztomel(f, htk=False)\n"
+"\n"
+"Convert a scalar from frequency to mel scale.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"m : float\n"
+" input frequency, in Hz\n"
+"htk : bool\n"
+" if `True`, use Htk mel scale instead of Slaney.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" output mel\n"
+"\n"
+"See Also\n"
+"--------\n"
+"meltohz\n"
+"";
+PyObject * Py_aubio_hztomel(PyObject *self, PyObject *args);
+
+static char Py_aubio_meltohz_doc[] = ""
+"meltohz(m, htk=False)\n"
+"\n"
+"Convert a scalar from mel scale to frequency.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"m : float\n"
+" input mel\n"
+"htk : bool\n"
+" if `True`, use Htk mel scale instead of Slaney.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+" output frequency, in Hz\n"
+"\n"
+"See Also\n"
+"--------\n"
+"hztomel\n"
+"";
+PyObject * Py_aubio_meltohz(PyObject *self, PyObject *args);
+
+static char Py_aubio_hztomel_htk_doc[] = ""
+"hztomel_htk(m)\n"
+"\n"
+"Same as `hztomel(m, htk=True)`\n"
+"\n"
+"See Also\n"
+"--------\n"
+"hztomel\n"
+"";
+PyObject * Py_aubio_hztomel_htk(PyObject *self, PyObject *args);
+
+static char Py_aubio_meltohz_htk_doc[] = ""
+"meltohz_htk(m)\n"
+"\n"
+"Same as `meltohz(m, htk=True)`\n"
+"\n"
+"See Also\n"
+"--------\n"
+"meltohz\n"
+"";
+PyObject * Py_aubio_meltohz_htk(PyObject *self, PyObject *args);
+
#endif /* PY_AUBIO_MUSICUTILS_H */
#include "aubio-types.h"
-static char Py_pvoc_doc[] = "pvoc object";
+static char Py_pvoc_doc[] = ""
+"pvoc(win_s=512, hop_s=256)\n"
+"\n"
+"Phase vocoder.\n"
+"\n"
+"`pvoc` creates callable object implements a phase vocoder [1]_,\n"
+"using the tricks detailed in [2]_.\n"
+"\n"
+"The call function takes one input of type `fvec` and of size\n"
+"`hop_s`, and returns a `cvec` of length `win_s//2+1`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"win_s : int\n"
+" number of channels in the phase-vocoder.\n"
+"hop_s : int\n"
+" number of samples expected between each call\n"
+"\n"
+"Examples\n"
+"--------\n"
+">>> x = aubio.fvec(256)\n"
+">>> pv = aubio.pvoc(512, 256)\n"
+">>> pv(x)\n"
+"aubio cvec of 257 elements\n"
+"\n"
+"Default values for hop_s and win_s are provided:\n"
+"\n"
+">>> pv = aubio.pvoc()\n"
+">>> pv.win_s, pv.hop_s\n"
+"512, 256\n"
+"\n"
+"A `cvec` can be resynthesised using `rdo()`:\n"
+"\n"
+">>> pv = aubio.pvoc(512, 256)\n"
+">>> y = aubio.cvec(512)\n"
+">>> x_reconstructed = pv.rdo(y)\n"
+">>> x_reconstructed.shape\n"
+"(256,)\n"
+"\n"
+"References\n"
+"----------\n"
+".. [1] James A. Moorer. The use of the phase vocoder in computer music\n"
+" applications. `Journal of the Audio Engineering Society`,\n"
+" 26(1/2):42–45, 1978.\n"
+".. [2] Amalia de Götzen, Nicolas Bernardini, and Daniel Arfib. Traditional\n"
+" (?) implementations of a phase vocoder: the tricks of the trade.\n"
+" In `Proceedings of the International Conference on Digital Audio\n"
+" Effects` (DAFx-00), pages 37–44, University of Verona, Italy, 2000.\n"
+" (`online version <"
+"https://www.cs.princeton.edu/courses/archive/spr09/cos325/Bernardini.pdf"
+">`_).\n"
+"";
+
typedef struct
{
self->win_s = Py_default_vector_length;
self->hop_s = Py_default_vector_length/2;
- if (self == NULL) {
- return NULL;
- }
-
if (win_s > 0) {
self->win_s = win_s;
} else if (win_s < 0) {
static PyMemberDef Py_pvoc_members[] = {
{"win_s", T_INT, offsetof (Py_pvoc, win_s), READONLY,
- "size of the window"},
+ "int: Size of phase vocoder analysis windows, in samples.\n"
+ ""},
{"hop_s", T_INT, offsetof (Py_pvoc, hop_s), READONLY,
- "size of the hop"},
+ "int: Interval between two analysis, in samples.\n"
+ ""},
{ NULL } // sentinel
};
static PyMethodDef Py_pvoc_methods[] = {
{"rdo", (PyCFunction) Py_pvoc_rdo, METH_VARARGS,
- "synthesis of spectral grain"},
- {"set_window", (PyCFunction) Pyaubio_pvoc_set_window, METH_VARARGS, ""},
+ "rdo(fftgrain)\n"
+ "\n"
+ "Read a new spectral grain and resynthesise the next `hop_s`\n"
+ "output samples.\n"
+ "\n"
+ "Parameters\n"
+ "----------\n"
+ "fftgrain : cvec\n"
+ " new input `cvec` to synthesize from, should be of size `win_s/2+1`\n"
+ "\n"
+ "Returns\n"
+ "-------\n"
+ "fvec\n"
+ " re-synthesised output of shape `(hop_s,)`\n"
+ "\n"
+ "Example\n"
+ "-------\n"
+ ">>> pv = aubio.pvoc(2048, 512)\n"
+ ">>> out = pv.rdo(aubio.cvec(2048))\n"
+ ">>> out.shape\n"
+ "(512,)\n"
+ ""},
+ {"set_window", (PyCFunction) Pyaubio_pvoc_set_window, METH_VARARGS,
+ "set_window(window_type)\n"
+ "\n"
+ "Set window function\n"
+ "\n"
+ "Parameters\n"
+ "----------\n"
+ "window_type : str\n"
+ " the window type to use for this phase vocoder\n"
+ "\n"
+ "Raises\n"
+ "------\n"
+ "ValueError\n"
+ " If an unknown window type was given.\n"
+ "\n"
+ "See Also\n"
+ "--------\n"
+ "window : create a window.\n"
+ ""},
{NULL}
};
} Py_sink;
static char Py_sink_doc[] = ""
-" __new__(path, samplerate = 44100, channels = 1)\n"
+"sink(path, samplerate=44100, channels=1)\n"
"\n"
-" Create a new sink, opening the given path for writing.\n"
+"Write audio samples to file.\n"
"\n"
-" Examples\n"
-" --------\n"
+"Parameters\n"
+"----------\n"
+"path : str\n"
+" Pathname of the file to be opened for writing.\n"
+"samplerate : int\n"
+" Sampling rate of the file, in Hz.\n"
+"channels : int\n"
+" Number of channels to create the file with.\n"
"\n"
-" Create a new sink at 44100Hz, mono:\n"
+"Examples\n"
+"--------\n"
"\n"
-" >>> sink('/tmp/t.wav')\n"
+"Create a new sink at 44100Hz, mono:\n"
"\n"
-" Create a new sink at 8000Hz, mono:\n"
+">>> snk = aubio.sink('out.wav')\n"
"\n"
-" >>> sink('/tmp/t.wav', samplerate = 8000)\n"
+"Create a new sink at 32000Hz, stereo, write 100 samples into it:\n"
"\n"
-" Create a new sink at 32000Hz, stereo:\n"
+">>> snk = aubio.sink('out.wav', samplerate=16000, channels=3)\n"
+">>> snk(aubio.fvec(100), 100)\n"
"\n"
-" >>> sink('/tmp/t.wav', samplerate = 32000, channels = 2)\n"
+"Open a new sink at 48000Hz, stereo, write `1234` samples into it:\n"
"\n"
-" Create a new sink at 32000Hz, 5 channels:\n"
+">>> with aubio.sink('out.wav', samplerate=48000, channels=2) as src:\n"
+"... snk(aubio.fvec(1024), 1024)\n"
+"... snk(aubio.fvec(210), 210)\n"
+"...\n"
"\n"
-" >>> sink('/tmp/t.wav', channels = 5, samplerate = 32000)\n"
-"\n"
-" __call__(vec, write)\n"
-" x(vec,write) <==> x.do(vec, write)\n"
-"\n"
-" Write vector to sink.\n"
-"\n"
-" See also\n"
-" --------\n"
-" aubio.sink.do\n"
+"See also\n"
+"--------\n"
+"source: read audio samples from a file.\n"
"\n";
static char Py_sink_do_doc[] = ""
-"x.do(vec, write) <==> x(vec, write)\n"
+"do(vec, write)\n"
+"\n"
+"Write a single channel vector to sink.\n"
"\n"
-"write monophonic vector to sink";
+"Parameters\n"
+"----------\n"
+"vec : fvec\n"
+" input vector `(n,)` where `n >= 0`.\n"
+"write : int\n"
+" Number of samples to write.\n"
+"";
static char Py_sink_do_multi_doc[] = ""
-"x.do_multi(mat, write)\n"
+"do_multi(mat, write)\n"
"\n"
-"write polyphonic vector to sink";
+"Write a matrix containing vectors from multiple channels to sink.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"mat : numpy.ndarray\n"
+" input matrix of shape `(channels, n)`, where `n >= 0`.\n"
+"write : int\n"
+" Number of frames to write.\n"
+"";
static char Py_sink_close_doc[] = ""
-"x.close()\n"
+"close()\n"
+"\n"
+"Close this sink now.\n"
"\n"
-"close this sink now";
+"By default, the sink will be closed before being deleted.\n"
+"Explicitely closing a sink can be useful to control the number\n"
+"of files simultaneously opened.\n"
+"";
static PyObject *
Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
static void
Py_sink_del (Py_sink *self, PyObject *unused)
{
- del_aubio_sink(self->o);
- free(self->mwrite_data.data);
+ if (self->o) {
+ del_aubio_sink(self->o);
+ free(self->mwrite_data.data);
+ }
if (self->uri) {
free(self->uri);
}
static PyMemberDef Py_sink_members[] = {
{"uri", T_STRING, offsetof (Py_sink, uri), READONLY,
- "path at which the sink was created"},
+ "str (read-only): Path at which the sink was created."},
{"samplerate", T_INT, offsetof (Py_sink, samplerate), READONLY,
- "samplerate at which the sink was created"},
+ "int (read-only): Samplerate at which the sink was created."},
{"channels", T_INT, offsetof (Py_sink, channels), READONLY,
- "number of channels with which the sink was created"},
+ "int (read-only): Number of channels with which the sink was created."},
{ NULL } // sentinel
};
} Py_source;
static char Py_source_doc[] = ""
-" __new__(path, samplerate = 0, hop_size = 512, channels = 1)\n"
+"source(path, samplerate=0, hop_size=512, channels=0)\n"
"\n"
-" Create a new source, opening the given path for reading.\n"
+"Read audio samples from a media file.\n"
"\n"
-" Examples\n"
-" --------\n"
+"`source` open the file specified in `path` and creates a callable\n"
+"returning `hop_size` new audio samples at each invocation.\n"
"\n"
-" Create a new source, using the original samplerate, with hop_size = 512:\n"
+"If `samplerate=0` (default), the original sampling rate of `path`\n"
+"will be used. Otherwise, the output audio samples will be\n"
+"resampled at the desired sampling-rate.\n"
"\n"
-" >>> source('/tmp/t.wav')\n"
+"If `channels=0` (default), the original number of channels\n"
+"in `path` will be used. Otherwise, the output audio samples\n"
+"will be down-mixed or up-mixed to the desired number of\n"
+"channels.\n"
"\n"
-" Create a new source, resampling the original to 8000Hz:\n"
+"If `path` is a URL, a remote connection will be attempted to\n"
+"open the resource and stream data from it.\n"
"\n"
-" >>> source('/tmp/t.wav', samplerate = 8000)\n"
+"The parameter `hop_size` determines how many samples should be\n"
+"read at each consecutive calls.\n"
"\n"
-" Create a new source, resampling it at 32000Hz, hop_size = 32:\n"
+"Parameters\n"
+"----------\n"
+"path : str\n"
+" pathname (or URL) of the file to be opened for reading\n"
+"samplerate : int, optional\n"
+" sampling rate of the file\n"
+"hop_size : int, optional\n"
+" number of samples to be read per iteration\n"
+"channels : int, optional\n"
+" number of channels of the file\n"
"\n"
-" >>> source('/tmp/t.wav', samplerate = 32000, hop_size = 32)\n"
+"Examples\n"
+"--------\n"
+"By default, when only `path` is given, the file will be opened\n"
+"with its original sampling rate and channel:\n"
"\n"
-" Create a new source, using its original samplerate:\n"
+">>> src = aubio.source('stereo.wav')\n"
+">>> src.uri, src.samplerate, src.channels, src.duration\n"
+"('stereo.wav', 48000, 2, 86833)\n"
"\n"
-" >>> source('/tmp/t.wav', samplerate = 0)\n"
+"A typical loop to read all samples from a local file could\n"
+"look like this:\n"
"\n"
-" __call__()\n"
-" vec, read = x() <==> vec, read = x.do()\n"
+">>> src = aubio.source('stereo.wav')\n"
+">>> total_read = 0\n"
+">>> while True:\n"
+"... samples, read = src()\n"
+"... # do something with samples\n"
+"... total_read += read\n"
+"... if read < src.hop_size:\n"
+"... break\n"
+"...\n"
"\n"
-" Read vector from source.\n"
+"In a more Pythonic way, it can also look like this:\n"
"\n"
-" See also\n"
-" --------\n"
-" aubio.source.do\n"
-"\n";
+">>> total_read = 0\n"
+">>> with aubio.source('stereo.wav') as src:\n"
+"... for frames in src:\n"
+"... total_read += samples.shape[-1]\n"
+"...\n"
+"\n"
+".. rubric:: Basic interface\n"
+"\n"
+"`source` is a **callable**; its :meth:`__call__` method\n"
+"returns a tuple containing:\n"
+"\n"
+"- a vector of shape `(hop_size,)`, filled with the `read` next\n"
+" samples available, zero-padded if `read < hop_size`\n"
+"- `read`, an integer indicating the number of samples read\n"
+"\n"
+"To read the first `hop_size` samples from the source, simply call\n"
+"the instance itself, with no argument:\n"
+"\n"
+">>> src = aubio.source('song.ogg')\n"
+">>> samples, read = src()\n"
+">>> samples.shape, read, src.hop_size\n"
+"((512,), 512, 512)\n"
+"\n"
+"The first call returned the slice of samples `[0 : hop_size]`.\n"
+"The next call will return samples `[hop_size: 2*hop_size]`.\n"
+"\n"
+"After several invocations of :meth:`__call__`, when reaching the end\n"
+"of the opened stream, `read` might become less than `hop_size`:\n"
+"\n"
+">>> samples, read = src()\n"
+">>> samples.shape, read\n"
+"((512,), 354)\n"
+"\n"
+"The end of the vector `samples` is filled with zeros.\n"
+"\n"
+"After the end of the stream, `read` will be `0` since no more\n"
+"samples are available:\n"
+"\n"
+">>> samples, read = src()\n"
+">>> samples.shape, read\n"
+"((512,), 0)\n"
+"\n"
+"**Note**: when the source has more than one channels, they\n"
+"are be down-mixed to mono when invoking :meth:`__call__`.\n"
+"To read from each individual channel, see :meth:`__next__`.\n"
+"\n"
+".. rubric:: ``for`` statements\n"
+"\n"
+"The `source` objects are **iterables**. This allows using them\n"
+"directly in a ``for`` loop, which calls :meth:`__next__` until\n"
+"the end of the stream is reached:\n"
+"\n"
+">>> src = aubio.source('stereo.wav')\n"
+">>> for frames in src:\n"
+">>> print (frames.shape)\n"
+"...\n"
+"(2, 512)\n"
+"(2, 512)\n"
+"(2, 230)\n"
+"\n"
+"**Note**: When `next(self)` is called on a source with multiple\n"
+"channels, an array of shape `(channels, read)` is returned,\n"
+"unlike with :meth:`__call__` which always returns the down-mixed\n"
+"channels.\n"
+"\n"
+"If the file is opened with a single channel, `next(self)` returns\n"
+"an array of shape `(read,)`:\n"
+"\n"
+">>> src = aubio.source('stereo.wav', channels=1)\n"
+">>> next(src).shape\n"
+"(512,)\n"
+"\n"
+".. rubric:: ``with`` statements\n"
+"\n"
+"The `source` objects are **context managers**, which allows using\n"
+"them in ``with`` statements:\n"
+"\n"
+">>> with aubio.source('audiotrack.wav') as source:\n"
+"... n_frames=0\n"
+"... for samples in source:\n"
+"... n_frames += len(samples)\n"
+"... print('read', n_frames, 'samples in', samples.shape[0], 'channels',\n"
+"... 'from file \"%%s\"' %% source.uri)\n"
+"...\n"
+"read 239334 samples in 2 channels from file \"audiotrack.wav\"\n"
+"\n"
+"The file will be closed before exiting the statement.\n"
+"\n"
+"See also the methods implementing the context manager,\n"
+":meth:`__enter__` and :meth:`__exit__`.\n"
+"\n"
+".. rubric:: Seeking and closing\n"
+"\n"
+"At any time, :meth:`seek` can be used to move to any position in\n"
+"the file. For instance, to rewind to the start of the stream:\n"
+"\n"
+">>> src.seek(0)\n"
+"\n"
+"The opened file will be automatically closed when the object falls\n"
+"out of scope and is scheduled for garbage collection.\n"
+"\n"
+"In some cases, it is useful to manually :meth:`close` a given source,\n"
+"for instance to limit the number of simultaneously opened files:\n"
+"\n"
+">>> src.close()\n"
+"\n"
+".. rubric:: Input formats\n"
+"\n"
+"Depending on how aubio was compiled, :class:`source` may or may not\n"
+"open certain **files format**. Below are some examples that assume\n"
+"support for compressed files and remote urls was compiled in:\n"
+"\n"
+"- open a local file using its original sampling rate and channels,\n"
+" and with the default hop size:\n"
+"\n"
+">>> s = aubio.source('sample.wav')\n"
+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
+"('sample.wav', 44100, 2, 512)\n"
+"\n"
+"- open a local compressed audio file, resampling to 32000Hz if needed:\n"
+"\n"
+">>> s = aubio.source('song.mp3', samplerate=32000)\n"
+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
+"('song.mp3', 32000, 2, 512)\n"
+"\n"
+"- open a local video file, down-mixing and resampling it to 16kHz:\n"
+"\n"
+">>> s = aubio.source('movie.mp4', samplerate=16000, channels=1)\n"
+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
+"('movie.mp4', 16000, 1, 512)\n"
+"\n"
+"- open a remote resource, with hop_size = 1024:\n"
+"\n"
+">>> s = aubio.source('https://aubio.org/drum.ogg', hop_size=1024)\n"
+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
+"('https://aubio.org/drum.ogg', 48000, 2, 1024)\n"
+"\n"
+"See Also\n"
+"--------\n"
+"sink: write audio samples to a file.\n"
+"";
static char Py_source_get_samplerate_doc[] = ""
-"x.get_samplerate() -> source samplerate\n"
+"get_samplerate()\n"
+"\n"
+"Get sampling rate of source.\n"
"\n"
-"Get samplerate of source.";
+"Returns\n"
+"-------\n"
+"int\n"
+" Sampling rate, in Hz.\n"
+"";
static char Py_source_get_channels_doc[] = ""
-"x.get_channels() -> number of channels\n"
+"get_channels()\n"
"\n"
-"Get number of channels in source.";
+"Get number of channels in source.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"int\n"
+" Number of channels.\n"
+"";
static char Py_source_do_doc[] = ""
-"vec, read = x.do() <==> vec, read = x()\n"
+"source.do()\n"
+"\n"
+"Read vector of audio samples.\n"
"\n"
-"Read monophonic vector from source.";
+"If the audio stream in the source has more than one channel,\n"
+"the channels will be down-mixed.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"samples : numpy.ndarray\n"
+" `fvec` of size `hop_size` containing the new samples.\n"
+"read : int\n"
+" Number of samples read from the source, equals to `hop_size`\n"
+" before the end-of-file is reached, less when it is reached,\n"
+" and `0` after.\n"
+"\n"
+"See Also\n"
+"--------\n"
+"do_multi\n"
+"\n"
+"Examples\n"
+"--------\n"
+">>> src = aubio.source('sample.wav', hop_size=1024)\n"
+">>> src.do()\n"
+"(array([-0.00123001, -0.00036685, 0.00097106, ..., -0.2031033 ,\n"
+" -0.2025854 , -0.20221856], dtype=" AUBIO_NPY_SMPL_STR "), 1024)\n"
+"";
static char Py_source_do_multi_doc[] = ""
-"mat, read = x.do_multi()\n"
+"do_multi()\n"
+"\n"
+"Read multiple channels of audio samples.\n"
"\n"
-"Read polyphonic vector from source.";
+"If the source was opened with the same number of channels\n"
+"found in the stream, each channel will be read individually.\n"
+"\n"
+"If the source was opened with less channels than the number\n"
+"of channels in the stream, only the first channels will be read.\n"
+"\n"
+"If the source was opened with more channels than the number\n"
+"of channel in the original stream, the first channels will\n"
+"be duplicated on the additional output channel.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"samples : numpy.ndarray\n"
+" NumPy array of shape `(hop_size, channels)` containing the new\n"
+" audio samples.\n"
+"read : int\n"
+" Number of samples read from the source, equals to `hop_size`\n"
+" before the end-of-file is reached, less when it is reached,\n"
+" and `0` after.\n"
+"\n"
+"See Also\n"
+"--------\n"
+"do\n"
+"\n"
+"Examples\n"
+"--------\n"
+">>> src = aubio.source('sample.wav')\n"
+">>> src.do_multi()\n"
+"(array([[ 0.00668335, 0.0067749 , 0.00714111, ..., -0.05737305,\n"
+" -0.05856323, -0.06018066],\n"
+" [-0.00842285, -0.0072937 , -0.00576782, ..., -0.09405518,\n"
+" -0.09558105, -0.09725952]], dtype=" AUBIO_NPY_SMPL_STR "), 512)\n"
+"";
static char Py_source_close_doc[] = ""
-"x.close()\n"
+"close()\n"
+"\n"
+"Close this source now.\n"
"\n"
-"Close this source now.";
+".. note:: Closing twice a source will **not** raise any exception.\n"
+"";
static char Py_source_seek_doc[] = ""
-"x.seek(position)\n"
+"seek(position)\n"
+"\n"
+"Seek to position in file.\n"
+"\n"
+"If the source was not opened with its original sampling-rate,\n"
+"`position` corresponds to the position in the re-sampled file.\n"
"\n"
-"Seek to resampled frame position.";
+"Parameters\n"
+"----------\n"
+"position : str\n"
+" position to seek to, in samples\n"
+"";
static PyObject *
Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
/* compute _do function */
aubio_source_do (self->o, &(self->c_read_to), &read);
+ if (PyErr_Occurred() != NULL) {
+ return NULL;
+ }
+
outputs = PyTuple_New(2);
PyTuple_SetItem( outputs, 0, self->read_to );
PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read));
/* compute _do function */
aubio_source_do_multi (self->o, &(self->c_mread_to), &read);
+ if (PyErr_Occurred() != NULL) {
+ return NULL;
+ }
+
outputs = PyTuple_New(2);
PyTuple_SetItem( outputs, 0, self->mread_to);
PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read));
static PyMemberDef Py_source_members[] = {
{"uri", T_STRING, offsetof (Py_source, uri), READONLY,
- "path at which the source was created"},
+ "str (read-only): pathname or URL"},
{"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY,
- "samplerate at which the source is viewed"},
+ "int (read-only): sampling rate"},
{"channels", T_INT, offsetof (Py_source, channels), READONLY,
- "number of channels found in the source"},
+ "int (read-only): number of channels"},
{"hop_size", T_INT, offsetof (Py_source, hop_size), READONLY,
- "number of consecutive frames that will be read at each do or do_multi call"},
+ "int (read-only): number of samples read per iteration"},
{"duration", T_INT, offsetof (Py_source, duration), READONLY,
- "total number of frames in the source (estimated)"},
+ "int (read-only): total number of frames in the source\n"
+ "\n"
+ "Can be estimated, for instance if the opened stream is\n"
+ "a compressed media or a remote resource.\n"
+ "\n"
+ "Example\n"
+ "-------\n"
+ ">>> n = 0\n"
+ ">>> src = aubio.source('track1.mp3')\n"
+ ">>> for samples in src:\n"
+ "... n += samples.shape[-1]\n"
+ "...\n"
+ ">>> n, src.duration\n"
+ "(9638784, 9616561)\n"
+ ""},
{ NULL } // sentinel
};
return vec;
} else if (PyLong_AsLong(size) > 0) {
// short read, return a shorter array
- PyArrayObject *shortread = (PyArrayObject*)PyTuple_GetItem(done, 0);
+ PyObject *vec = PyTuple_GetItem(done, 0);
+ // take a copy to prevent resizing internal arrays
+ PyArrayObject *shortread = (PyArrayObject*)PyArray_FROM_OTF(vec,
+ NPY_NOTYPE, NPY_ARRAY_ENSURECOPY);
PyArray_Dims newdims;
PyObject *reshaped;
newdims.len = PyArray_NDIM(shortread);
}
reshaped = PyArray_Newshape(shortread, &newdims, NPY_CORDER);
Py_DECREF(shortread);
+ Py_DECREF(vec);
return reshaped;
} else {
PyErr_SetNone(PyExc_StopIteration);
//NPY_OBJECT, NPY_OBJECT,
};
-static char Py_unwrap2pi_doc[] = "map angle to unit circle [-pi, pi[";
+// Note: these docstrings should *not* include the function signatures
+
+static char Py_unwrap2pi_doc[] = ""
+"\n"
+"Map angle to unit circle :math:`[-\\pi, \\pi[`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"x : numpy.ndarray\n"
+" input array\n"
+"\n"
+"Returns\n"
+"-------\n"
+"numpy.ndarray\n"
+" values clamped to the unit circle :math:`[-\\pi, \\pi[`\n"
+"";
static void* Py_unwrap2pi_data[] = {
(void *)aubio_unwrap2pi,
//(void *)unwrap2pio,
};
-static char Py_freqtomidi_doc[] = "convert frequency to midi";
+static char Py_freqtomidi_doc[] = ""
+"\n"
+"Convert frequency `[0; 23000[` to midi `[0; 128[`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"x : numpy.ndarray\n"
+" Array of frequencies, in Hz.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"numpy.ndarray\n"
+" Converted frequencies, in midi note.\n"
+"";
static void* Py_freqtomidi_data[] = {
(void *)aubio_freqtomidi,
(void *)aubio_freqtomidi,
};
-static char Py_miditofreq_doc[] = "convert midi to frequency";
+static char Py_miditofreq_doc[] = ""
+"\n"
+"Convert midi `[0; 128[` to frequency `[0, 23000]`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"x : numpy.ndarray\n"
+" Array of frequencies, in midi note.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"numpy.ndarray\n"
+" Converted frequencies, in Hz\n"
+"";
static void* Py_miditofreq_data[] = {
(void *)aubio_miditofreq,
#! /usr/bin/env python
+# -*- coding: utf8 -*-
+
+"""
+aubio
+=====
+
+Provides a number of classes and functions for music and audio signal
+analysis.
+
+How to use the documentation
+----------------------------
+
+Documentation of the python module is available as docstrings provided
+within the code, and a reference guide available online from `the
+aubio homepage <https://aubio.org/documentation>`_.
+
+The docstrings examples are written assuming `aubio` and `numpy` have been
+imported with:
+
+>>> import aubio
+>>> import numpy as np
+"""
import numpy
from ._aubio import __version__ as version
from .midiconv import *
from .slicing import *
+
class fvec(numpy.ndarray):
- """a numpy vector holding audio samples"""
+ """fvec(input_arg=1024)
+ A vector holding float samples.
- def __new__(cls, input_arg=1024, **kwargs):
+ If `input_arg` is an `int`, a 1-dimensional vector of length `input_arg`
+ will be created and filled with zeros. Otherwise, if `input_arg` is an
+ `array_like` object, it will be converted to a 1-dimensional vector of
+ type :data:`float_type`.
+
+ Parameters
+ ----------
+ input_arg : `int` or `array_like`
+ Can be a positive integer, or any object that can be converted to
+ a numpy array with :func:`numpy.array`.
+
+ Examples
+ --------
+ >>> aubio.fvec(10)
+ array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)
+ >>> aubio.fvec([0,1,2])
+ array([0., 1., 2.], dtype=float32)
+ >>> a = np.arange(10); type(a), type(aubio.fvec(a))
+ (<class 'numpy.ndarray'>, <class 'numpy.ndarray'>)
+ >>> a.dtype, aubio.fvec(a).dtype
+ (dtype('int64'), dtype('float32'))
+
+ Notes
+ -----
+
+ In the Python world, `fvec` is simply a subclass of
+ :class:`numpy.ndarray`. In practice, any 1-dimensional `numpy.ndarray` of
+ `dtype` :data:`float_type` may be passed to methods accepting
+ `fvec` as parameter. For instance, `sink()` or `pvoc()`.
+
+ See Also
+ --------
+ cvec : a container holding spectral data
+ numpy.ndarray : parent class of :class:`fvec`
+ numpy.zeros : create a numpy array filled with zeros
+ numpy.array : create a numpy array from an existing object
+ """
+ def __new__(cls, input_arg=1024):
if isinstance(input_arg, int):
if input_arg == 0:
raise ValueError("vector length of 1 or more expected")
- return numpy.zeros(input_arg, dtype=float_type, **kwargs)
+ return numpy.zeros(input_arg, dtype=float_type, order='C')
else:
- return numpy.array(input_arg, dtype=float_type, **kwargs)
+ np_input = numpy.array(input_arg, dtype=float_type, order='C')
+ if len(np_input.shape) != 1:
+ raise ValueError("input_arg should have shape (n,)")
+ if np_input.shape[0] == 0:
+ raise ValueError("vector length of 1 or more expected")
+ return np_input
import sys
import argparse
+import warnings
import aubio
def aubio_parser():
help='estimate midi-like notes (monophonic)')
subparser.add_input()
subparser.add_buf_hop_size()
+ subparser.add_silence()
+ subparser.add_release_drop()
subparser.add_time_format()
subparser.add_verbose_help()
subparser.set_defaults(process=process_notes)
subparser.set_defaults(process=process_quiet)
def parser_add_subcommand_cut(subparsers):
- # quiet subcommand
+ # cut subcommand
subparser = subparsers.add_parser('cut',
help='slice at timestamps')
subparser.add_input()
help="samplerate at which the file should be represented")
def add_verbose_help(self):
- self.add_argument("-v","--verbose",
+ self.add_argument("-v", "--verbose",
action="count", dest="verbose", default=1,
help="make lots of noise [default]")
- self.add_argument("-q","--quiet",
+ self.add_argument("-q", "--quiet",
action="store_const", dest="verbose", const=0,
help="be quiet")
self.add_hop_size(hop_size=hop_size)
def add_buf_size(self, buf_size=512):
- self.add_argument("-B","--bufsize",
+ self.add_argument("-B", "--bufsize",
action="store", dest="buf_size", default=buf_size,
metavar = "<size>", type=int,
help="buffer size [default=%d]" % buf_size)
def add_hop_size(self, hop_size=256):
- self.add_argument("-H","--hopsize",
+ self.add_argument("-H", "--hopsize",
metavar = "<size>", type=int,
action="store", dest="hop_size", default=hop_size,
help="overlap size [default=%d]" % hop_size)
def add_method(self, method='default', helpstr='method'):
- self.add_argument("-m","--method",
+ self.add_argument("-m", "--method",
metavar = "<method>", type=str,
action="store", dest="method", default=method,
help="%s [default=%s]" % (helpstr, method))
def add_threshold(self, default=None):
- self.add_argument("-t","--threshold",
+ self.add_argument("-t", "--threshold",
metavar = "<threshold>", type=float,
action="store", dest="threshold", default=default,
help="threshold [default=%s]" % default)
action="store", dest="silence", default=-70,
help="silence threshold")
+ def add_release_drop(self):
+ self.add_argument("-d", "--release-drop",
+ metavar = "<value>", type=float,
+ action="store", dest="release_drop", default=10,
+ help="release drop threshold")
+
def add_minioi(self, default="12ms"):
self.add_argument("-M", "--minioi",
metavar = "<value>", type=str,
help=helpstr)
def add_slicer_options(self):
- self.add_argument("-o","--output", type = str,
+ self.add_argument("-o", "--output", type = str,
metavar = "<outputdir>",
action="store", dest="output_directory", default=None,
- help="specify path where slices of the original file should be created")
+ help="specify path where slices of the original file should"
+ " be created")
self.add_argument("--cut-until-nsamples", type = int,
metavar = "<samples>",
action = "store", dest = "cut_until_nsamples", default = None,
- help="how many extra samples should be added at the end of each slice")
+ help="how many extra samples should be added at the end of"
+ " each slice")
self.add_argument("--cut-every-nslices", type = int,
metavar = "<samples>",
action = "store", dest = "cut_every_nslices", default = None,
self.add_argument("--cut-until-nslices", type = int,
metavar = "<slices>",
action = "store", dest = "cut_until_nslices", default = None,
- help="how many extra slices should be added at the end of each slice")
+ help="how many extra slices should be added at the end of"
+ " each slice")
+ self.add_argument("--create-first",
+ action = "store_true", dest = "create_first", default = False,
+ help="always include first slice")
# some utilities
self.time2string = timefunc(args.time_format)
if args.verbose > 2 and hasattr(self, 'options'):
name = type(self).__name__.split('_')[1]
- optstr = ' '.join(['running', name, 'with options', repr(self.options), '\n'])
+ optstr = ' '.join(['running', name, 'with options',
+ repr(self.options), '\n'])
sys.stderr.write(optstr)
def flush(self, frames_read, samplerate):
# optionally called at the end of process
def parse_options(self, args, valid_opts):
# get any valid options found in a dictionnary of arguments
- options = {k :v for k,v in vars(args).items() if k in valid_opts}
+ options = {k: v for k, v in vars(args).items() if k in valid_opts}
self.options = options
def remap_pvoc_options(self, options):
if len(self.beat_locations) < 2:
outstr = "unknown bpm"
else:
- bpms = 60./ np.diff(self.beat_locations)
+ bpms = 60. / np.diff(self.beat_locations)
median_bpm = np.mean(bpms)
if len(self.beat_locations) < 10:
outstr = "%.2f bpm (uncertain)" % median_bpm
def __init__(self, args):
self.parse_options(args, self.valid_opts)
self.notes = aubio.notes(**self.options)
+ if args.silence is not None:
+ self.notes.set_silence(args.silence)
+ if args.release_drop is not None:
+ self.notes.set_release_drop(args.release_drop)
super(process_notes, self).__init__(args)
def __call__(self, block):
return self.notes(block)
def repr_res(self, res, frames_read, samplerate):
- if res[2] != 0: # note off
+ if res[2] != 0: # note off
fmt_out = self.time2string(frames_read, samplerate)
sys.stdout.write(fmt_out + '\n')
- if res[0] != 0: # note on
+ if res[0] != 0: # note on
lastmidi = res[0]
fmt_out = "%f\t" % lastmidi
fmt_out += self.time2string(frames_read, samplerate)
- sys.stdout.write(fmt_out) # + '\t')
+ sys.stdout.write(fmt_out) # + '\t')
def flush(self, frames_read, samplerate):
eof = self.time2string(frames_read, samplerate)
sys.stdout.write(eof + '\n')
if aubio.silence_detection(block, self.silence) == 1:
if self.wassilence != 1:
self.wassilence = 1
- return 2 # newly found silence
- return 1 # silence again
+ return 2 # newly found silence
+ return 1 # silence again
else:
if self.wassilence != 0:
self.wassilence = 0
- return -1 # newly found noise
- return 0 # noise again
+ return -1 # newly found noise
+ return 0 # noise again
def repr_res(self, res, frames_read, samplerate):
fmt_out = None
def __call__(self, block):
ret = super(process_cut, self).__call__(block)
- if ret: self.slices.append(self.onset.get_last())
+ if ret:
+ self.slices.append(self.onset.get_last())
return ret
def flush(self, frames_read, samplerate):
- from aubio.cut import _cut_slice
_cut_slice(self.options, self.slices)
- duration = float (frames_read) / float(samplerate)
- base_info = '%(source_file)s' % {'source_file': self.options.source_uri}
+ duration = float(frames_read) / float(samplerate)
+ base_info = '%(source_file)s' % \
+ {'source_file': self.options.source_uri}
base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
- {'duration': duration, 'samplerate': samplerate}
+ {'duration': duration, 'samplerate': samplerate}
info = "created %d slices from " % len(self.slices)
info += base_info
sys.stderr.write(info)
+def _cut_slice(options, timestamps):
+ # cutting pass
+ nstamps = len(timestamps)
+ if nstamps > 0:
+ # generate output files
+ timestamps_end = None
+ if options.cut_every_nslices:
+ timestamps = timestamps[::options.cut_every_nslices]
+ nstamps = len(timestamps)
+ if options.cut_until_nslices and options.cut_until_nsamples:
+ msg = "using cut_until_nslices, but cut_until_nsamples is set"
+ warnings.warn(msg)
+ if options.cut_until_nsamples:
+ lag = options.cut_until_nsamples
+ timestamps_end = [t + lag for t in timestamps[1:]]
+ timestamps_end += [1e120]
+ if options.cut_until_nslices:
+ slice_lag = options.cut_until_nslices
+ timestamps_end = [t for t in timestamps[1 + slice_lag:]]
+ timestamps_end += [1e120] * (options.cut_until_nslices + 1)
+ aubio.slice_source_at_stamps(options.source_uri,
+ timestamps, timestamps_end = timestamps_end,
+ output_dir = options.output_directory,
+ samplerate = options.samplerate,
+ create_first = options.create_first)
+
def main():
parser = aubio_parser()
- args = parser.parse_args()
+ if sys.version_info[0] != 3:
+ # on py2, create a dummy ArgumentParser to workaround the
+ # optional subcommand issue. See https://bugs.python.org/issue9253
+ # This ensures that:
+ # - version string is shown when only '-V' is passed
+ # - help is printed if '-V' is passed with any other argument
+ # - any other argument get forwarded to the real parser
+ parser_root = argparse.ArgumentParser(add_help=False)
+ parser_root.add_argument('-V', '--version', help="show version",
+ action="store_true", dest="show_version")
+ args, extras = parser_root.parse_known_args()
+ if not args.show_version: # no -V, forward to parser
+ args = parser.parse_args(extras, namespace=args)
+ elif len(extras) != 0: # -V with other arguments, print help
+ parser.print_help()
+ sys.exit(1)
+ else: # in py3, we can simply use parser directly
+ args = parser.parse_args()
if 'show_version' in args and args.show_version:
sys.stdout.write('aubio version ' + aubio.version + '\n')
sys.exit(0)
elif 'verbose' in args and args.verbose > 3:
sys.stderr.write('aubio version ' + aubio.version + '\n')
- if 'command' not in args or args.command is None or args.command in ['help']:
+ if 'command' not in args or args.command is None \
+ or args.command in ['help']:
# no command given, print help and return 1
parser.print_help()
if args.command and args.command in ['help']:
# increment total number of frames read
frames_read += read
# exit loop at end of file
- if read < a_source.hop_size: break
+ if read < a_source.hop_size:
+ break
# flush the processor if needed
processor.flush(frames_read, a_source.samplerate)
if args.verbose > 1:
fmt_string += " ({:d} samples in {:d} blocks of {:d})"
fmt_string += " from {:s} at {:d}Hz\n"
sys.stderr.write(fmt_string.format(
- frames_read/float(a_source.samplerate),
+ frames_read / float(a_source.samplerate),
frames_read,
frames_read // a_source.hop_size + 1,
a_source.hop_size,
"""
import sys
-from aubio.cmd import AubioArgumentParser
+from aubio.cmd import AubioArgumentParser, _cut_slice
def aubio_cut_parser():
parser = AubioArgumentParser()
parser.add_input()
- parser.add_argument("-O","--onset-method",
+ parser.add_argument("-O", "--onset-method",
action="store", dest="onset_method", default='default',
metavar = "<onset_method>",
help="onset detection method [default=default] \
complexdomain|hfc|phase|specdiff|energy|kl|mkl")
# cutting methods
- parser.add_argument("-b","--beat",
+ parser.add_argument("-b", "--beat",
action="store_true", dest="beat", default=False,
help="slice at beat locations")
"""
- parser.add_argument("-S","--silencecut",
+ parser.add_argument("-S", "--silencecut",
action="store_true", dest="silencecut", default=False,
help="use silence locations")
- parser.add_argument("-s","--silence",
+ parser.add_argument("-s", "--silence",
metavar = "<value>",
action="store", dest="silence", default=-70,
help="silence threshold [default=-70]")
"""
# algorithm parameters
parser.add_buf_hop_size()
- parser.add_argument("-t","--threshold", "--onset-threshold",
+ parser.add_argument("-t", "--threshold", "--onset-threshold",
metavar = "<threshold>", type=float,
action="store", dest="threshold", default=0.3,
help="onset peak picking threshold [default=0.3]")
- parser.add_argument("-c","--cut",
+ parser.add_argument("-c", "--cut",
action="store_true", dest="cut", default=False,
help="cut input sound file at detected labels")
parser.add_minioi()
"""
- parser.add_argument("-D","--delay",
+ parser.add_argument("-D", "--delay",
action = "store", dest = "delay", type = float,
metavar = "<seconds>", default=0,
help="number of seconds to take back [default=system]\
default system delay is 3*hopsize/samplerate")
- parser.add_argument("-C","--dcthreshold",
+ parser.add_argument("-C", "--dcthreshold",
metavar = "<value>",
action="store", dest="dcthreshold", default=1.,
help="onset peak picking DC component [default=1.]")
- parser.add_argument("-L","--localmin",
+ parser.add_argument("-L", "--localmin",
action="store_true", dest="localmin", default=False,
help="use local minima after peak detection")
- parser.add_argument("-d","--derivate",
+ parser.add_argument("-d", "--derivate",
action="store_true", dest="derivate", default=False,
help="derivate onset detection function")
- parser.add_argument("-z","--zerocross",
+ parser.add_argument("-z", "--zerocross",
metavar = "<value>",
action="store", dest="zerothres", default=0.008,
help="zero-crossing threshold for slicing [default=0.00008]")
# plotting functions
- parser.add_argument("-p","--plot",
+ parser.add_argument("-p", "--plot",
action="store_true", dest="plot", default=False,
help="draw plot")
- parser.add_argument("-x","--xsize",
+ parser.add_argument("-x", "--xsize",
metavar = "<size>",
action="store", dest="xsize", default=1.,
type=float, help="define xsize for plot")
- parser.add_argument("-y","--ysize",
+ parser.add_argument("-y", "--ysize",
metavar = "<size>",
action="store", dest="ysize", default=1.,
type=float, help="define ysize for plot")
- parser.add_argument("-f","--function",
+ parser.add_argument("-f", "--function",
action="store_true", dest="func", default=False,
help="print detection function")
- parser.add_argument("-n","--no-onsets",
+ parser.add_argument("-n", "--no-onsets",
action="store_true", dest="nplot", default=False,
help="do not plot detected onsets")
- parser.add_argument("-O","--outplot",
+ parser.add_argument("-O", "--outplot",
metavar = "<output_image>",
action="store", dest="outplot", default=None,
help="save plot to output.{ps,png}")
- parser.add_argument("-F","--spectrogram",
+ parser.add_argument("-F", "--spectrogram",
action="store_true", dest="spectro", default=False,
help="add spectrogram to the plot")
"""
s = source(source_uri, samplerate, hopsize)
if samplerate == 0:
- samplerate = s.get_samplerate()
+ samplerate = s.samplerate
options.samplerate = samplerate
if options.beat:
- o = tempo(options.onset_method, bufsize, hopsize, samplerate=samplerate)
+ o = tempo(options.onset_method, bufsize, hopsize,
+ samplerate=samplerate)
else:
- o = onset(options.onset_method, bufsize, hopsize, samplerate=samplerate)
+ o = onset(options.onset_method, bufsize, hopsize,
+ samplerate=samplerate)
if options.minioi:
if options.minioi.endswith('ms'):
o.set_minioi_ms(int(options.minioi[:-2]))
while True:
samples, read = s()
if o(samples):
- timestamps.append (o.get_last())
- if options.verbose: print ("%.4f" % o.get_last_s())
+ timestamps.append(o.get_last())
+ if options.verbose:
+ print("%.4f" % o.get_last_s())
total_frames += read
- if read < hopsize: break
+ if read < hopsize:
+ break
del s
return timestamps, total_frames
-def _cut_slice(options, timestamps):
- # cutting pass
- nstamps = len(timestamps)
- if nstamps > 0:
- # generate output files
- from aubio.slicing import slice_source_at_stamps
- timestamps_end = None
- if options.cut_every_nslices:
- timestamps = timestamps[::options.cut_every_nslices]
- nstamps = len(timestamps)
- if options.cut_until_nslices and options.cut_until_nsamples:
- print ("warning: using cut_until_nslices, but cut_until_nsamples is set")
- if options.cut_until_nsamples:
- timestamps_end = [t + options.cut_until_nsamples for t in timestamps[1:]]
- timestamps_end += [ 1e120 ]
- if options.cut_until_nslices:
- timestamps_end = [t for t in timestamps[1 + options.cut_until_nslices:]]
- timestamps_end += [ 1e120 ] * (options.cut_until_nslices + 1)
- slice_source_at_stamps(options.source_uri,
- timestamps, timestamps_end = timestamps_end,
- output_dir = options.output_directory,
- samplerate = options.samplerate)
-
def main():
parser = aubio_cut_parser()
options = parser.parse_args()
timestamps, total_frames = _cut_analyze(options)
# print some info
- duration = float (total_frames) / float(options.samplerate)
+ duration = float(total_frames) / float(options.samplerate)
base_info = '%(source_uri)s' % {'source_uri': options.source_uri}
base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
{'duration': duration, 'samplerate': options.samplerate}
# -*- coding: utf-8 -*-
""" utilities to convert midi note number to and from note names """
-__all__ = ['note2midi', 'midi2note', 'freq2note']
-
import sys
+from ._aubio import freqtomidi, miditofreq
+
+__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
+
py3 = sys.version_info[0] == 3
if py3:
str_instances = str
str_instances = (str, unicode)
int_instances = (int, long)
+
def note2midi(note):
- " convert note name to midi note number, e.g. [C-1, G9] -> [0, 127] "
- _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
+ """Convert note name to midi note number.
+
+ Input string `note` should be composed of one note root
+ and one octave, with optionally one modifier in between.
+
+ List of valid components:
+
+ - note roots: `C`, `D`, `E`, `F`, `G`, `A`, `B`,
+ - modifiers: `b`, `#`, as well as unicode characters
+ `𝄫`, `♭`, `♮`, `♯` and `𝄪`,
+ - octave numbers: `-1` -> `11`.
+
+ Parameters
+ ----------
+ note : str
+ note name
+
+ Returns
+ -------
+ int
+ corresponding midi note number
+
+ Examples
+ --------
+ >>> aubio.note2midi('C#4')
+ 61
+ >>> aubio.note2midi('B♭5')
+ 82
+
+ Raises
+ ------
+ TypeError
+ If `note` was not a string.
+ ValueError
+ If an error was found while converting `note`.
+
+ See Also
+ --------
+ midi2note, freqtomidi, miditofreq
+ """
+ _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7,
+ 'A': 9, 'B': 11}
_valid_modifiers = {
- u'𝄫': -2, # double flat
- u'♭': -1, 'b': -1, '\u266d': -1, # simple flat
- u'♮': 0, '\u266e': 0, None: 0, # natural
- '#': +1, u'♯': +1, '\u266f': +1, # sharp
- u'𝄪': +2, # double sharp
+ u'𝄫': -2, # double flat
+ u'♭': -1, 'b': -1, '\u266d': -1, # simple flat
+ u'♮': 0, '\u266e': 0, None: 0, # natural
+ '#': +1, u'♯': +1, '\u266f': +1, # sharp
+ u'𝄪': +2, # double sharp
}
_valid_octaves = range(-1, 10)
if not isinstance(note, str_instances):
- raise TypeError("a string is required, got %s (%s)" % (note, str(type(note))))
+ msg = "a string is required, got {:s} ({:s})"
+ raise TypeError(msg.format(str(type(note)), repr(note)))
if len(note) not in range(2, 5):
- raise ValueError("string of 2 to 4 characters expected, got %d (%s)" \
- % (len(note), note))
- notename, modifier, octave = [None]*3
+ msg = "string of 2 to 4 characters expected, got {:d} ({:s})"
+ raise ValueError(msg.format(len(note), note))
+ notename, modifier, octave = [None] * 3
if len(note) == 4:
notename, modifier, octave_sign, octave = note
if octave not in _valid_octaves:
raise ValueError("%s is not a valid octave" % octave)
- midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier]
+ midi = (octave + 1) * 12 + _valid_notenames[notename] \
+ + _valid_modifiers[modifier]
if midi > 127:
raise ValueError("%s is outside of the range C-2 to G8" % note)
return midi
+
def midi2note(midi):
- " convert midi note number to note name, e.g. [0, 127] -> [C-1, G9] "
+ """Convert midi note number to note name.
+
+ Parameters
+ ----------
+ midi : int [0, 128]
+ input midi note number
+
+ Returns
+ -------
+ str
+ note name
+
+ Examples
+ --------
+ >>> aubio.midi2note(70)
+ 'A#4'
+ >>> aubio.midi2note(59)
+ 'B3'
+
+ Raises
+ ------
+ TypeError
+ If `midi` was not an integer.
+ ValueError
+ If `midi` is out of the range `[0, 128]`.
+
+ See Also
+ --------
+ note2midi, miditofreq, freqtomidi
+ """
if not isinstance(midi, int_instances):
raise TypeError("an integer is required, got %s" % midi)
if midi not in range(0, 128):
- raise ValueError("an integer between 0 and 127 is excepted, got %d" % midi)
- _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
+ msg = "an integer between 0 and 127 is excepted, got {:d}"
+ raise ValueError(msg.format(midi))
+ _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#',
+ 'A', 'A#', 'B']
return _valid_notenames[midi % 12] + str(int(midi / 12) - 1)
+
def freq2note(freq):
- " convert frequency in Hz to nearest note name, e.g. [0, 22050.] -> [C-1, G9] "
- from aubio import freqtomidi
- return midi2note(int(freqtomidi(freq)))
+ """Convert frequency in Hz to nearest note name.
+
+ Parameters
+ ----------
+ freq : float [0, 23000[
+ input frequency, in Hz
+
+ Returns
+ -------
+ str
+ name of the nearest note
+
+ Example
+ -------
+ >>> aubio.freq2note(440)
+ 'A4'
+ >>> aubio.freq2note(220.1)
+ 'A3'
+ """
+ nearest_note = int(freqtomidi(freq) + .5)
+ return midi2note(nearest_note)
+
+
+def note2freq(note):
+ """Convert note name to corresponding frequency, in Hz.
+
+ Parameters
+ ----------
+ note : str
+ input note name
+
+ Returns
+ -------
+ freq : float [0, 23000[
+ frequency, in Hz
+
+ Example
+ -------
+ >>> aubio.note2freq('A4')
+ 440
+ >>> aubio.note2freq('A3')
+ 220.1
+ """
+ midi = note2midi(note)
+ return miditofreq(midi)
_max_timestamp = 1e120
+
def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,
- output_dir=None, samplerate=0, hopsize=256):
- """ slice a sound file at given timestamps """
+ output_dir=None, samplerate=0, hopsize=256,
+ create_first=False):
+ """Slice a sound file at given timestamps.
+
+ This function reads `source_file` and creates slices, new smaller
+ files each starting at `t` in `timestamps`, a list of integer
+ corresponding to time locations in `source_file`, in samples.
+
+ If `timestamps_end` is unspecified, the slices will end at
+ `timestamps_end[n] = timestamps[n+1]-1`, or the end of file.
+ Otherwise, `timestamps_end` should be a list with the same length
+ as `timestamps` containing the locations of the end of each slice.
+
+ If `output_dir` is unspecified, the new slices will be written in
+ the current directory. If `output_dir` is a string, new slices
+ will be written in `output_dir`, after creating the directory if
+ required.
+
+ The default `samplerate` is 0, meaning the original sampling rate
+ of `source_file` will be used. When using a sampling rate
+ different to the one of the original files, `timestamps` and
+ `timestamps_end` should be expressed in the re-sampled signal.
+
+ The `hopsize` parameter simply tells :class:`source` to use this
+ hopsize and does not change the output slices.
+
+ If `create_first` is True and `timestamps` does not start with `0`, the
+ first slice from `0` to `timestamps[0] - 1` will be automatically added.
+
+ Parameters
+ ----------
+ source_file : str
+ path of the resource to slice
+ timestamps : :obj:`list` of :obj:`int`
+ time stamps at which to slice, in samples
+ timestamps_end : :obj:`list` of :obj:`int` (optional)
+ time stamps at which to end the slices
+ output_dir : str (optional)
+ output directory to write the slices to
+ samplerate : int (optional)
+ samplerate to read the file at
+ hopsize : int (optional)
+ number of samples read from source per iteration
+ create_first : bool (optional)
+ always create the slice at the start of the file
+
+ Examples
+ --------
+ Create two slices: the first slice starts at the beginning of the
+ input file `loop.wav` and lasts exactly one second, starting at
+ sample `0` and ending at sample `44099`; the second slice starts
+ at sample `44100` and lasts until the end of the input file:
+
+ >>> aubio.slice_source_at_stamps('loop.wav', [0, 44100])
+
+ Create one slice, from 1 second to 2 seconds:
+
+ >>> aubio.slice_source_at_stamps('loop.wav', [44100], [44100 * 2 - 1])
+
+ Notes
+ -----
+ Slices may be overlapping. If `timestamps_end` is `1` element
+ shorter than `timestamps`, the last slice will end at the end of
+ the file.
+ """
- if timestamps is None or len(timestamps) == 0:
+ if not timestamps:
raise ValueError("no timestamps given")
- if timestamps[0] != 0:
+ if timestamps[0] != 0 and create_first:
timestamps = [0] + timestamps
if timestamps_end is not None:
timestamps_end = [timestamps[1] - 1] + timestamps_end
if timestamps_end is not None:
- if len(timestamps_end) != len(timestamps):
+ if len(timestamps_end) == len(timestamps) - 1:
+ timestamps_end = timestamps_end + [_max_timestamp]
+ elif len(timestamps_end) != len(timestamps):
raise ValueError("len(timestamps_end) != len(timestamps)")
else:
timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]
regions = list(zip(timestamps, timestamps_end))
- #print regions
source_base_name, _ = os.path.splitext(os.path.basename(source_file))
if output_dir is not None:
os.makedirs(output_dir)
source_base_name = os.path.join(output_dir, source_base_name)
- def new_sink_name(source_base_name, timestamp, samplerate):
- """ create a sink based on a timestamp in samples, converted in seconds """
+ def _new_sink_name(source_base_name, timestamp, samplerate):
+ # create name based on a timestamp in samples, converted in seconds
timestamp_seconds = timestamp / float(samplerate)
return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'
# get hopsize new samples from source
vec, read = _source.do_multi()
# if the total number of frames read will exceed the next region start
- if len(regions) and total_frames + read >= regions[0][0]:
- #print "getting", regions[0], "at", total_frames
+ while regions and total_frames + read >= regions[0][0]:
# get next region
start_stamp, end_stamp = regions.pop(0)
# create a name for the sink
- new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate)
+ new_sink_path = _new_sink_name(source_base_name, start_stamp,
+ samplerate)
# create its sink
_sink = sink(new_sink_path, samplerate, _source.channels)
# create a dictionary containing all this
- new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink}
+ new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp,
+ 'sink': _sink}
# append the dictionary to the current list of slices
slices.append(new_slice)
start = max(start_stamp - total_frames, 0)
# number of samples yet to written be until end of region
remaining = end_stamp - total_frames + 1
- #print current_slice, remaining, start
# not enough frames remaining, time to split
if remaining < read:
if remaining > start:
# write remaining samples from current region
_sink.do_multi(vec[:, start:remaining], remaining - start)
- #print "closing region", "remaining", remaining
# close this file
_sink.close()
elif read > start:
# write all the samples
_sink.do_multi(vec[:, start:read], read - start)
total_frames += read
+ # remove old slices
+ slices = list(filter(lambda s: s['end_stamp'] > total_frames,
+ slices))
if read < hopsize:
break
# we have some clean up to do
'buf_size': 'Py_default_vector_length',
'win_s': 'Py_default_vector_length',
+ 'size': 'Py_default_vector_length',
# and here too
'hop_size': 'Py_default_vector_length / 2',
'hop_s': 'Py_default_vector_length / 2',
'tempo': '1',
'filterbank': 'self->n_filters',
'tss': 'self->buf_size',
+ 'dct': 'self->size',
}
objinputsize = {
self.do_inputs = [get_params_types_names(self.do_proto)[1]]
self.do_outputs = get_params_types_names(self.do_proto)[2:]
struct_output_str = ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in self.do_outputs]
+ if len(self.prototypes['rdo']):
+ rdo_outputs = get_params_types_names(prototypes['rdo'][0])[2:]
+ struct_output_str += ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in rdo_outputs]
+ self.outputs += rdo_outputs
self.struct_outputs = ";\n ".join(struct_output_str)
#print ("input_params: ", map(split_type, get_input_params(self.do_proto)))
out += self.gen_init()
out += self.gen_del()
out += self.gen_do()
+ if len(self.prototypes['rdo']):
+ self.do_proto = self.prototypes['rdo'][0]
+ self.do_inputs = [get_params_types_names(self.do_proto)[1]]
+ self.do_outputs = get_params_types_names(self.do_proto)[2:]
+ out += self.gen_do(method='rdo')
out += self.gen_memberdef()
out += self.gen_set()
out += self.gen_get()
return out.format(do_inputs_list = do_inputs_list, **self.__dict__)
def gen_doc(self):
+ sig = []
+ for p in self.input_params:
+ name = p['name']
+ defval = aubiodefvalue[name].replace('"','\\\"')
+ sig.append("{name}={defval}".format(defval=defval, name=name))
out = """
-// TODO: add documentation
-static char Py_{shortname}_doc[] = \"undefined\";
+#ifndef PYAUBIO_{shortname}_doc
+#define PYAUBIO_{shortname}_doc "{shortname}({sig})"
+#endif /* PYAUBIO_{shortname}_doc */
+
+static char Py_{shortname}_doc[] = ""
+PYAUBIO_{shortname}_doc
+"";
"""
- return out.format(**self.__dict__)
+ return out.format(sig=', '.join(sig), **self.__dict__)
def gen_new(self):
out = """
""".format(del_fn = del_fn)
return out
- def gen_do(self):
+ def gen_do(self, method = 'do'):
out = """
// do {shortname}
static PyObject*
-Py_{shortname}_do (Py_{shortname} * self, PyObject * args)
-{{""".format(**self.__dict__)
+Pyaubio_{shortname}_{method} (Py_{shortname} * self, PyObject * args)
+{{""".format(method = method, **self.__dict__)
input_params = self.do_inputs
output_params = self.do_outputs
#print input_params
// {shortname} setters
""".format(**self.__dict__)
for set_param in self.prototypes['set']:
- params = get_params_types_names(set_param)[1]
- paramtype = params['type']
+ params = get_params_types_names(set_param)[1:]
+ param = self.shortname.split('_set_')[-1]
+ paramdecls = "".join(["""
+ {0} {1};""".format(p['type'], p['name']) for p in params])
method_name = get_name(set_param)
param = method_name.split('aubio_'+self.shortname+'_set_')[-1]
- pyparamtype = pyargparse_chars[paramtype]
+ refs = ", ".join(["&%s" % p['name'] for p in params])
+ paramlist = ", ".join(["%s" % p['name'] for p in params])
+ if len(params):
+ paramlist = "," + paramlist
+ pyparamtypes = ''.join([pyargparse_chars[p['type']] for p in params])
out += """
static PyObject *
Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args)
{{
uint_t err = 0;
- {paramtype} {param};
+ {paramdecls}
+""".format(param = param, paramdecls = paramdecls, **self.__dict__)
+
+ if len(refs) and len(pyparamtypes):
+ out += """
- if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{
+ if (!PyArg_ParseTuple (args, "{pyparamtypes}", {refs})) {{
return NULL;
}}
- err = aubio_{shortname}_set_{param} (self->o, {param});
+""".format(pyparamtypes = pyparamtypes, refs = refs)
+
+ out += """
+ err = aubio_{shortname}_set_{param} (self->o {paramlist});
if (err > 0) {{
- PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}");
+ if (PyErr_Occurred() == NULL) {{
+ PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}");
+ }} else {{
+ // change the RuntimeError into ValueError
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_Restore(PyExc_ValueError, value, traceback);
+ }}
return NULL;
}}
Py_RETURN_NONE;
}}
-""".format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__)
+""".format(param = param, refs = refs, paramdecls = paramdecls,
+ pyparamtypes = pyparamtypes, paramlist = paramlist, **self.__dict__)
return out
def gen_get(self):
out += """
{{"{shortname}", (PyCFunction) Py{name},
METH_NOARGS, ""}},""".format(name = name, shortname = shortname)
+ for m in self.prototypes['rdo']:
+ name = get_name(m)
+ shortname = name.replace('aubio_%s_' % self.shortname, '')
+ out += """
+ {{"{shortname}", (PyCFunction) Py{name},
+ METH_VARARGS, ""}},""".format(name = name, shortname = shortname)
out += """
{NULL} /* sentinel */
};
0,
0,
0,
- (ternaryfunc)Py_{shortname}_do,
+ (ternaryfunc)Pyaubio_{shortname}_do,
0,
0,
0,
import os
import subprocess
import glob
+from distutils.sysconfig import customize_compiler
+from gen_code import MappedObject
header = os.path.join('src', 'aubio.h')
output_path = os.path.join('python', 'gen')
def get_preprocessor():
# findout which compiler to use
- from distutils.sysconfig import customize_compiler
compiler_name = distutils.ccompiler.get_default_compiler()
compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)
try:
cpp_cmd = compiler.cc.split()
cpp_cmd += ['-E']
+ # On win-amd64 (py3.x), the default compiler is cross-compiling, from x86
+ # to amd64 with %WIN_SDK_ROOT%\x86_amd64\cl.exe, but using this binary as a
+ # pre-processor generates no output, so we use %WIN_SDK_ROOT%\cl.exe
+ # instead.
+ if len(cpp_cmd) > 1 and 'cl.exe' in cpp_cmd[-2]:
+ plat = os.path.basename(os.path.dirname(cpp_cmd[-2]))
+ if plat == 'x86_amd64':
+ print('workaround on win64 to avoid empty pre-processor output')
+ cpp_cmd[-2] = cpp_cmd[-2].replace('x86_amd64', '')
+ elif True in ['amd64' in f for f in cpp_cmd]:
+ print('warning: not using workaround for', cpp_cmd[0], plat)
+
if not cpp_cmd:
print("Warning: could not guess preprocessor, using env's CC")
cpp_cmd = os.environ.get('CC', 'cc').split()
cpp_cmd += ['-E']
- cpp_cmd += ['-x', 'c'] # force C language (emcc defaults to c++)
+ if 'emcc' in cpp_cmd:
+ cpp_cmd += ['-x', 'c'] # emcc defaults to c++, force C language
return cpp_cmd
def get_c_declarations(header=header, usedouble=False):
''' return a dense and preprocessed string of all c declarations implied by aubio.h
'''
+ cpp_output = get_cpp_output(header=header, usedouble=usedouble)
+ return filter_cpp_output (cpp_output)
+
+
+def get_cpp_output(header=header, usedouble=False):
+ ''' find and run a C pre-processor on aubio.h '''
cpp_cmd = get_preprocessor()
macros = [('AUBIO_UNSTABLE', 1)]
print("Running command: {:s}".format(" ".join(cpp_cmd)))
proc = subprocess.Popen(cpp_cmd,
stderr=subprocess.PIPE,
- stdout=subprocess.PIPE)
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
assert proc, 'Proc was none'
cpp_output = proc.stdout.read()
err_output = proc.stderr.read()
+ if err_output:
+ print("Warning: preprocessor produced errors or warnings:\n%s" \
+ % err_output)
if not cpp_output:
- raise Exception("preprocessor output is empty:\n%s" % err_output)
- elif err_output:
- print("Warning: preprocessor produced warnings:\n%s" % err_output)
+ raise_msg = "preprocessor output is empty! Running command " \
+ + "\"%s\" failed" % " ".join(cpp_cmd)
+ if err_output:
+ raise_msg += " with stderr: \"%s\"" % err_output
+ else:
+ raise_msg += " with no stdout or stderr"
+ raise Exception(raise_msg)
if not isinstance(cpp_output, list):
- cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')]
+ cpp_output = [l.strip() for l in cpp_output.split('\n')]
- cpp_output = filter(lambda y: len(y) > 1, cpp_output)
+ return cpp_output
+
+def filter_cpp_output(cpp_raw_output):
+ ''' prepare cpp-output for parsing '''
+ cpp_output = filter(lambda y: len(y) > 1, cpp_raw_output)
cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output))
i = 1
if o[:6] == 'aubio_':
shortname = o[6:-2] # without aubio_ prefix and _t suffix
- lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []}
+ lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'rdo': [], 'get': [], 'set': [], 'other': []}
lib[shortname]['longname'] = o
lib[shortname]['shortname'] = shortname
lib[shortname]['struct'].append(fn)
elif '_do' in fn:
lib[shortname]['do'].append(fn)
+ elif '_rdo' in fn:
+ lib[shortname]['rdo'].append(fn)
elif 'new_' in fn:
lib[shortname]['new'].append(fn)
elif 'del_' in fn:
# print_c_declarations_results(lib, c_declarations)
sources_list = []
- try:
- from .gen_code import MappedObject
- except (SystemError, ValueError):
- from gen_code import MappedObject
for o in lib:
out = source_header
mapped = MappedObject(lib[o], usedouble=usedouble)
#
import sys, os, glob, subprocess
import distutils, distutils.command.clean, distutils.dir_util
-from .gen_external import generate_external, header, output_path
+from gen_external import generate_external, header, output_path
from this_version import get_aubio_version
# define macros (waf puts them in build/src/config.h)
for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H',
'HAVE_MATH_H', 'HAVE_STRING_H',
- 'HAVE_C99_VARARGS_MACROS',
+ 'HAVE_ERRNO_H', 'HAVE_C99_VARARGS_MACROS',
'HAVE_LIMITS_H', 'HAVE_STDARG_H',
'HAVE_MEMCPY_HACKS']:
ext.define_macros += [(define_macro, 1)]
--- /dev/null
+"""
+This file imports test methods from different testing modules, in this
+order:
+
+ - try importing 'pytest'
+ - if it fails, fallback to 'numpy.testing'
+
+Nose2 support was removed because of lacking assertWarns on py2.7.
+
+"""
+
+import sys
+
+_has_pytest = False
+
+# check if we have pytest
+try:
+ import pytest
+ parametrize = pytest.mark.parametrize
+ assert_raises = pytest.raises
+ assert_warns = pytest.warns
+ skipTest = pytest.skip
+ _has_pytest = True
+ def run_module_suite():
+ import sys, pytest
+ pytest.main(sys.argv)
+except:
+ pass
+
+# otherwise fallback on numpy.testing
+if not _has_pytest:
+ from numpy.testing import dec, assert_raises, assert_warns
+ from numpy.testing import SkipTest
+ parametrize = dec.parametrize
+ def skipTest(msg):
+ raise SkipTest(msg)
+ from numpy.testing import run_module_suite
+
+# always use numpy's assert_equal
+import numpy
+assert_equal = numpy.testing.assert_equal
+++ /dev/null
-#! /usr/bin/env python
-
-if __name__ == '__main__':
- import nose2.main
- nose2.discover()
#! /usr/bin/env python
-from unittest import main
from numpy.testing import TestCase
class aubiomodule_test_case(TestCase):
self.assertEqual('0', aubio.version[0])
if __name__ == '__main__':
+ from unittest import main
main()
-
#! /usr/bin/env python
-import aubio.cmd
-from nose2 import main
from numpy.testing import TestCase
+import aubio.cmd
class aubio_cmd(TestCase):
class aubio_cmd_utils(TestCase):
def test_samples2seconds(self):
- self.assertEqual(aubio.cmd.samples2seconds(3200, 32000), "0.100000\t")
+ self.assertEqual(aubio.cmd.samples2seconds(3200, 32000),
+ "0.100000\t")
def test_samples2milliseconds(self):
- self.assertEqual(aubio.cmd.samples2milliseconds(3200, 32000), "100.000000\t")
+ self.assertEqual(aubio.cmd.samples2milliseconds(3200, 32000),
+ "100.000000\t")
def test_samples2samples(self):
- self.assertEqual(aubio.cmd.samples2samples(3200, 32000), "3200\t")
+ self.assertEqual(aubio.cmd.samples2samples(3200, 32000),
+ "3200\t")
if __name__ == '__main__':
+ from unittest import main
main()
#! /usr/bin/env python
import aubio.cut
-from nose2 import main
from numpy.testing import TestCase
class aubio_cut(TestCase):
assert self.a_parser.parse_args(['-v']).verbose
if __name__ == '__main__':
+ from unittest import main
main()
#! /usr/bin/env python
-from unittest import main
import numpy as np
from numpy.testing import TestCase, assert_equal
from aubio import cvec, fvec, float_type
a.norm = np.zeros((512//2+1, 2), dtype = float_type)
if __name__ == '__main__':
+ from unittest import main
main()
--- /dev/null
+#! /usr/bin/env python
+
+
+import numpy as np
+from numpy.testing import TestCase, assert_almost_equal
+import aubio
+
+precomputed_arange = [ 9.89949512, -6.44232273, 0., -0.67345482, 0.,
+ -0.20090288, 0., -0.05070186]
+
+precomputed_some_ones = [ 4.28539848, 0.2469689, -0.14625292, -0.58121818,
+ -0.83483052, -0.75921834, -0.35168475, 0.24087936,
+ 0.78539824, 1.06532764, 0.97632152, 0.57164496, 0.03688532,
+ -0.39446154, -0.54619485, -0.37771079]
+
+class aubio_dct(TestCase):
+
+ def test_init(self):
+ """ test that aubio.dct() is created with expected size """
+ a_dct = aubio.dct()
+ self.assertEqual(a_dct.size, 1024)
+
+ def test_arange(self):
+ """ test that dct(arange(8)) is computed correctly
+
+ >>> from scipy.fftpack import dct
+ >>> a_in = np.arange(8).astype(aubio.float_type)
+ >>> precomputed = dct(a_in, norm='ortho')
+ """
+ N = len(precomputed_arange)
+ a_dct = aubio.dct(8)
+ a_in = np.arange(8).astype(aubio.float_type)
+ a_expected = aubio.fvec(precomputed_arange)
+ assert_almost_equal(a_dct(a_in), a_expected, decimal=5)
+
+ def test_some_ones(self):
+ """ test that dct(somevector) is computed correctly """
+ a_dct = aubio.dct(16)
+ a_in = np.ones(16).astype(aubio.float_type)
+ a_in[1] = 0
+ a_in[3] = np.pi
+ a_expected = aubio.fvec(precomputed_some_ones)
+ assert_almost_equal(a_dct(a_in), a_expected, decimal=6)
+
+ def test_reconstruction(self):
+ """ test that some_ones vector can be recontructed """
+ a_dct = aubio.dct(16)
+ a_in = np.ones(16).astype(aubio.float_type)
+ a_in[1] = 0
+ a_in[3] = np.pi
+ a_dct_in = a_dct(a_in)
+ a_dct_reconstructed = a_dct.rdo(a_dct_in)
+ assert_almost_equal(a_dct_reconstructed, a_in, decimal=6)
+
+ def test_negative_size(self):
+ """ test that creation fails with a negative size """
+ with self.assertRaises(ValueError):
+ aubio.dct(-1)
+
+ def test_wrong_size(self):
+ """ test that creation fails with a non power-of-two size """
+ # supports for non 2** fft sizes only when compiled with fftw3
+ size = 13
+ try:
+ with self.assertRaises(RuntimeError):
+ aubio.dct(size)
+ except AssertionError:
+ self.skipTest('creating aubio.dct with size %d did not fail' % size)
+
+if __name__ == '__main__':
+ from unittest import main
+ main()
#! /usr/bin/env python
-from unittest import main
from numpy.testing import TestCase
from numpy.testing import assert_equal, assert_almost_equal
import numpy as np
assert_almost_equal ( r[0], impulse, decimal = 6)
assert_almost_equal ( r[1:], 0)
+class aubio_fft_odd_sizes(TestCase):
+
+ def test_reconstruct_with_odd_size(self):
+ win_s = 29
+ self.recontruct(win_s, 'odd sizes not supported')
+
+ def test_reconstruct_with_radix15(self):
+ win_s = 2 ** 4 * 15
+ self.recontruct(win_s, 'radix 15 supported')
+
+ def test_reconstruct_with_radix5(self):
+ win_s = 2 ** 4 * 5
+ self.recontruct(win_s, 'radix 5 supported')
+
+ def test_reconstruct_with_radix3(self):
+ win_s = 2 ** 4 * 3
+ self.recontruct(win_s, 'radix 3 supported')
+
+ def recontruct(self, win_s, skipMessage):
+ try:
+ f = fft(win_s)
+ except RuntimeError:
+ self.skipTest(skipMessage)
+ input_signal = fvec(win_s)
+ input_signal[win_s//2] = 1
+ c = f(input_signal)
+ output_signal = f.rdo(c)
+ assert_almost_equal(input_signal, output_signal)
+
+class aubio_fft_wrong_params(TestCase):
+
def test_large_input_timegrain(self):
win_s = 1024
f = fft(win_s)
with self.assertRaises(ValueError):
f.rdo(s)
-class aubio_fft_wrong_params(TestCase):
-
def test_wrong_buf_size(self):
win_s = -1
with self.assertRaises(ValueError):
fft(win_s)
- def test_buf_size_not_power_of_two(self):
- # when compiled with fftw3, aubio supports non power of two fft sizes
- win_s = 320
- try:
- with self.assertRaises(RuntimeError):
- fft(win_s)
- except AssertionError:
- self.skipTest('creating aubio.fft with size %d did not fail' % win_s)
-
def test_buf_size_too_small(self):
win_s = 1
with self.assertRaises(RuntimeError):
fft(win_s)
if __name__ == '__main__':
+ from unittest import main
main()
#! /usr/bin/env python
-from unittest import main
from numpy.testing import TestCase, assert_equal, assert_almost_equal
from aubio import fvec, digital_filter
-from .utils import array_from_text_file
+from utils import array_from_text_file
class aubio_filter_test_case(TestCase):
with self.assertRaises(ValueError):
f.set_biquad(0., 0., 0, 0., 0.)
+ def test_all_available_presets(self):
+ f = digital_filter(7)
+ for sr in [8000, 11025, 16000, 22050, 24000, 32000,
+ 44100, 48000, 88200, 96000, 192000]:
+ f.set_a_weighting(sr)
+ f = digital_filter(5)
+ for sr in [8000, 11025, 16000, 22050, 24000, 32000,
+ 44100, 48000, 88200, 96000, 192000]:
+ f.set_c_weighting(sr)
+
class aubio_filter_wrong_params(TestCase):
def test_negative_order(self):
digital_filter(-1)
if __name__ == '__main__':
+ from unittest import main
main()
from numpy.testing import TestCase, assert_equal, assert_almost_equal
from aubio import cvec, filterbank, float_type
-from .utils import array_from_text_file
+from utils import array_from_text_file
class aubio_filterbank_test_case(TestCase):
f(cvec(256))
if __name__ == '__main__':
- import nose2
- nose2.main()
+ from unittest import main
+ main()
import numpy as np
from numpy.testing import TestCase, assert_equal, assert_almost_equal
+from _tools import assert_warns
-from aubio import cvec, filterbank, float_type
-
-import warnings
-warnings.filterwarnings('ignore', category=UserWarning, append=True)
+from aubio import fvec, cvec, filterbank, float_type
class aubio_filterbank_mel_test_case(TestCase):
[ 0.02070313, 0.02138672, 0.02127604, 0.02135417,
0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])
+ def test_triangle_freqs_with_zeros(self):
+ """make sure set_triangle_bands works when list starts with 0"""
+ freq_list = [0, 40, 80]
+ freqs = np.array(freq_list, dtype = float_type)
+ f = filterbank(len(freqs)-2, 1024)
+ f.set_triangle_bands(freqs, 48000)
+ assert_equal ( f(cvec(1024)), 0)
+ self.assertIsInstance(f.get_coeffs(), np.ndarray)
+
+ def test_triangle_freqs_with_wrong_negative(self):
+ """make sure set_triangle_bands fails when list contains a negative"""
+ freq_list = [-10, 0, 80]
+ f = filterbank(len(freq_list)-2, 1024)
+ with self.assertRaises(ValueError):
+ f.set_triangle_bands(fvec(freq_list), 48000)
+
+ def test_triangle_freqs_with_wrong_ordering(self):
+ """make sure set_triangle_bands fails when list not ordered"""
+ freq_list = [0, 80, 40]
+ f = filterbank(len(freq_list)-2, 1024)
+ with self.assertRaises(ValueError):
+ f.set_triangle_bands(fvec(freq_list), 48000)
+
+ def test_triangle_freqs_with_large_freq(self):
+ """make sure set_triangle_bands warns when freq > nyquist"""
+ samplerate = 22050
+ freq_list = [0, samplerate//4, samplerate // 2 + 1]
+ f = filterbank(len(freq_list)-2, 1024)
+ with assert_warns(UserWarning):
+ f.set_triangle_bands(fvec(freq_list), samplerate)
+
+ def test_triangle_freqs_with_not_enough_filters(self):
+ """make sure set_triangle_bands warns when not enough filters"""
+ samplerate = 22050
+ freq_list = [0, 100, 1000, 4000, 8000, 10000]
+ f = filterbank(len(freq_list)-3, 1024)
+ with assert_warns(UserWarning):
+ f.set_triangle_bands(fvec(freq_list), samplerate)
+
+ def test_triangle_freqs_with_too_many_filters(self):
+ """make sure set_triangle_bands warns when too many filters"""
+ samplerate = 22050
+ freq_list = [0, 100, 1000, 4000, 8000, 10000]
+ f = filterbank(len(freq_list)-1, 1024)
+ with assert_warns(UserWarning):
+ f.set_triangle_bands(fvec(freq_list), samplerate)
+
+ def test_triangle_freqs_with_double_value(self):
+ """make sure set_triangle_bands works with 2 duplicate freqs"""
+ samplerate = 22050
+ freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000]
+ f = filterbank(len(freq_list)-2, 1024)
+ with assert_warns(UserWarning):
+ f.set_triangle_bands(fvec(freq_list), samplerate)
+
+ def test_triangle_freqs_with_triple(self):
+ """make sure set_triangle_bands works with 3 duplicate freqs"""
+ samplerate = 22050
+ freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000]
+ f = filterbank(len(freq_list)-2, 1024)
+ with assert_warns(UserWarning):
+ f.set_triangle_bands(fvec(freq_list), samplerate)
+
+
+ def test_triangle_freqs_without_norm(self):
+ """make sure set_triangle_bands works without """
+ samplerate = 22050
+ freq_list = fvec([0, 100, 1000, 10000])
+ f = filterbank(len(freq_list) - 2, 1024)
+ f.set_norm(0)
+ f.set_triangle_bands(freq_list, samplerate)
+ expected = f.get_coeffs()
+ f.set_norm(1)
+ f.set_triangle_bands(fvec(freq_list), samplerate)
+ assert_almost_equal(f.get_coeffs().T,
+ expected.T * 2. / (freq_list[2:] - freq_list[:-2]))
+
+ def test_triangle_freqs_wrong_norm(self):
+ f = filterbank(10, 1024)
+ with self.assertRaises(ValueError):
+ f.set_norm(-1)
+
+ def test_triangle_freqs_with_power(self):
+ f = filterbank(9, 1024)
+ freqs = fvec([40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000,
+ 24000])
+ f.set_power(2)
+ f.set_triangle_bands(freqs, 48000)
+ spec = cvec(1024)
+ spec.norm[:] = .1
+ expected = fvec([0.02070313, 0.02138672, 0.02127604, 0.02135417,
+ 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])
+ expected /= 100.
+ assert_almost_equal(f(spec), expected)
+
+ def test_mel_coeffs(self):
+ f = filterbank(40, 1024)
+ f.set_mel_coeffs(44100, 0, 44100 / 2)
+
+ def test_zero_fmax(self):
+ f = filterbank(40, 1024)
+ f.set_mel_coeffs(44100, 0, 0)
+
+ def test_wrong_mel_coeffs(self):
+ f = filterbank(40, 1024)
+ with self.assertRaises(ValueError):
+ f.set_mel_coeffs_slaney(0)
+ with self.assertRaises(ValueError):
+ f.set_mel_coeffs(44100, 0, -44100 / 2)
+ with self.assertRaises(ValueError):
+ f.set_mel_coeffs(44100, -0.1, 44100 / 2)
+ with self.assertRaises(ValueError):
+ f.set_mel_coeffs(-44100, 0.1, 44100 / 2)
+ with self.assertRaises(ValueError):
+ f.set_mel_coeffs_htk(-1, 0, 0)
+
+ def test_mel_coeffs_htk(self):
+ f = filterbank(40, 1024)
+ f.set_mel_coeffs_htk(44100, 0, 44100 / 2)
+
+
if __name__ == '__main__':
- import nose2
- nose2.main()
+ from unittest import main
+ main()
#! /usr/bin/env python
-from unittest import main
import numpy as np
from numpy.testing import TestCase, assert_equal, assert_almost_equal
from aubio import fvec, zero_crossing_rate, alpha_norm, min_removal
self.assertRaises(IndexError, a.__getitem__, 3)
self.assertRaises(IndexError, a.__getitem__, 2)
+ def test_wrong_dimensions(self):
+ a = np.array([[[1, 2], [3, 4]]], dtype=float_type)
+ self.assertRaises(ValueError, fvec, a)
+
+ def test_wrong_size(self):
+ a = np.ndarray([0,], dtype=float_type)
+ self.assertRaises(ValueError, fvec, a)
+
class aubio_wrong_fvec_input(TestCase):
""" uses min_removal to test PyAubio_IsValidVector """
del c
if __name__ == '__main__':
+ from unittest import main
main()
--- /dev/null
+#! /usr/bin/env python
+
+import numpy as np
+from numpy.testing import TestCase, assert_equal
+import aubio
+
+class aubio_shift_test_case(TestCase):
+
+ def run_shift_ishift(self, n):
+ ramp = np.arange(n, dtype=aubio.float_type)
+ # construct expected output
+ # even length: [5. 6. 7. 8. 9. 0. 1. 2. 3. 4.]
+ # odd length: [4. 5. 6. 0. 1. 2. 3.]
+ half = n - n//2
+ expected = np.concatenate([np.arange(half, n), np.arange(half)])
+ # shift in place, returns modified copy
+ assert_equal(aubio.shift(ramp), expected)
+ # check input was changed as expected
+ assert_equal(ramp, expected)
+ # construct expected output
+ expected = np.arange(n)
+ # revert shift in place, returns modifed copy
+ assert_equal(aubio.ishift(ramp), expected)
+ # check input was shifted back
+ assert_equal(ramp, expected)
+
+ def test_can_shift_fvec(self):
+ self.run_shift_ishift(10)
+
+ def test_can_shift_fvec_odd(self):
+ self.run_shift_ishift(7)
+
+if __name__ == '__main__':
+ from unittest import main
+ main()
--- /dev/null
+#! /usr/bin/env python
+
+from unittest import main
+from numpy.testing import TestCase
+from numpy.testing import assert_equal, assert_almost_equal
+from _tools import assert_warns
+import numpy as np
+import aubio
+
+from aubio import hztomel, meltohz
+from aubio import hztomel_htk, meltohz_htk
+
+
+class aubio_hztomel_test_case(TestCase):
+
+ def test_hztomel(self):
+ assert_equal(hztomel(0.), 0.)
+ assert_almost_equal(hztomel(400. / 3.), 2., decimal=5)
+ assert_almost_equal(hztomel(1000. / 3), 5.)
+ assert_equal(hztomel(200.), 3.)
+ assert_almost_equal(hztomel(1000.), 15)
+ assert_almost_equal(hztomel(6400), 42)
+ assert_almost_equal(hztomel(40960), 69)
+
+ for m in np.linspace(0, 1000, 100):
+ assert_almost_equal(hztomel(meltohz(m)) - m, 0, decimal=3)
+
+ def test_meltohz(self):
+ assert_equal(meltohz(0.), 0.)
+ assert_almost_equal(meltohz(2), 400. / 3., decimal=4)
+ assert_equal(meltohz(3.), 200.)
+ assert_almost_equal(meltohz(5), 1000. / 3., decimal=4)
+ assert_almost_equal(meltohz(15), 1000., decimal=4)
+ assert_almost_equal(meltohz(42), 6400., decimal=2)
+ assert_almost_equal(meltohz(69), 40960., decimal=1)
+
+ for f in np.linspace(0, 20000, 1000):
+ assert_almost_equal(meltohz(hztomel(f)) - f, 0, decimal=1)
+
+ def test_meltohz_negative(self):
+ with assert_warns(UserWarning):
+ assert_equal(meltohz(-1), 0)
+
+ def test_hztomel_negative(self):
+ with assert_warns(UserWarning):
+ assert_equal(hztomel(-1), 0)
+
+
+class aubio_hztomel_htk_test_case(TestCase):
+
+ def test_meltohz(self):
+ assert_equal(meltohz(0, htk=True), 0)
+ assert_almost_equal(meltohz(2595, htk=True), 6300., decimal=1)
+
+ def test_hztomel(self):
+ assert_equal(hztomel(0, htk=True), 0)
+ assert_almost_equal(hztomel(3428.7, htk=True), 2000., decimal=1)
+ assert_almost_equal(hztomel(6300, htk=True), 2595., decimal=1)
+
+ def test_meltohz_negative(self):
+ with assert_warns(UserWarning):
+ assert_equal(meltohz(-1, htk=True), 0)
+ assert_almost_equal(meltohz(2000, htk=True), 3428.7, decimal=1)
+ assert_almost_equal(meltohz(1000, htk=True), 1000., decimal=1)
+
+ def test_hztomel_negative(self):
+ with assert_warns(UserWarning):
+ assert_equal(meltohz(-1, htk=True), 0)
+ with assert_warns(UserWarning):
+ assert_equal(hztomel(-1, htk=True), 0)
+ assert_almost_equal(hztomel(1000, htk=True), 1000., decimal=1)
+
+ def test_hztomel_htk(self):
+ for f in np.linspace(0, 20000, 1000):
+ assert_almost_equal(meltohz_htk(hztomel_htk(f)) - f, 0, decimal=1)
+ for f in np.linspace(0, 20000, 1000):
+ assert_almost_equal(hztomel_htk(meltohz_htk(f)) - f, 0, decimal=1)
+
+
+class aubio_hztomel_wrong_values(TestCase):
+ """ more tests to cover all branches """
+
+ def test_hztomel_wrong_values(self):
+ with self.assertRaises(TypeError):
+ hztomel('s')
+
+ def test_meltohz_wrong_values(self):
+ with self.assertRaises(TypeError):
+ meltohz(bytes('ad'))
+
+ def test_meltohz_no_arg(self):
+ with self.assertRaises(TypeError):
+ meltohz()
+
+ def test_meltohz_htk_no_arg(self):
+ with self.assertRaises(TypeError):
+ meltohz_htk()
+
+ def test_hztomel_htk_wrong_values(self):
+ with self.assertRaises(TypeError):
+ hztomel_htk('0')
+
+ def test_hztomel_htk_false(self):
+ assert hztomel(120, htk=False) == hztomel(120)
+
+ def test_meltohz_htk_false(self):
+ assert meltohz(12, htk=False) == meltohz(12)
+
+
+if __name__ == '__main__':
+ from unittest import main
+ main()
#! /usr/bin/env python
-from unittest import main
from numpy.testing import TestCase, assert_equal
from numpy import array, arange, isnan, isinf
from aubio import bintomidi, miditobin, freqtobin, bintofreq, freqtomidi, miditofreq
assert_equal ( array(b) < 0, False )
if __name__ == '__main__':
+ from unittest import main
main()
#! /usr/bin/env python
-from nose2 import main
-from nose2.tools import params
+from _tools import parametrize, assert_raises
from numpy import random, count_nonzero
from numpy.testing import TestCase
from aubio import mfcc, cvec, float_type
new_params = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate']
new_deflts = [1024, 40, 13, 44100]
-class aubio_mfcc(TestCase):
+class Test_aubio_mfcc(object):
- def setUp(self):
- self.o = mfcc()
+ members_args = 'name'
- def test_default_creation(self):
- pass
-
- def test_delete(self):
- del self.o
-
- @params(*new_params)
+ @parametrize(members_args, new_params)
def test_read_only_member(self, name):
- o = self.o
- with self.assertRaises((TypeError, AttributeError)):
+ o = mfcc()
+ with assert_raises((TypeError, AttributeError)):
setattr(o, name, 0)
- @params(*zip(new_params, new_deflts))
+ @parametrize('name, expected', zip(new_params, new_deflts))
def test_default_param(self, name, expected):
""" test mfcc.{:s} = {:d} """.format(name, expected)
- o = self.o
- self.assertEqual( getattr(o, name), expected)
+ o = mfcc()
+ assert getattr(o, name) == expected
class aubio_mfcc_wrong_params(TestCase):
#print coeffs
-class aubio_mfcc_all_parameters(TestCase):
+class Test_aubio_mfcc_all_parameters(object):
- @params(
+ run_values = [
(2048, 40, 13, 44100),
(1024, 40, 13, 44100),
(512, 40, 13, 44100),
#(1024, 30, 20, 44100),
(1024, 40, 40, 44100),
(1024, 40, 3, 44100),
- )
+ ]
+ run_args = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate']
+
+ @parametrize(run_args, run_values)
def test_run_with_params(self, buf_size, n_filters, n_coeffs, samplerate):
" check mfcc can run with reasonable parameters "
o = mfcc(buf_size, n_filters, n_coeffs, samplerate)
o(spec)
#print coeffs
+
+class aubio_mfcc_fb_params(TestCase):
+
+ def test_set_scale(self):
+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+ m.set_scale(10.5)
+ assert m.get_scale() == 10.5
+ m(cvec(buf_size))
+
+ def test_set_power(self):
+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+ m.set_power(2.5)
+ assert m.get_power() == 2.5
+ m(cvec(buf_size))
+
+ def test_set_mel_coeffs(self):
+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+ m.set_mel_coeffs(0., samplerate/2.)
+ m(cvec(buf_size))
+
+ def test_set_mel_coeffs_htk(self):
+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+ m.set_mel_coeffs_htk(0., samplerate/2.)
+ m(cvec(buf_size))
+
+ def test_set_mel_coeffs_slaney(self):
+ buf_size, n_filters, n_coeffs, samplerate = 512, 40, 10, 16000
+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+ m.set_mel_coeffs_slaney()
+ m(cvec(buf_size))
+ assert m.get_power() == 1
+ assert m.get_scale() == 1
+
if __name__ == '__main__':
- main()
+ from _tools import run_module_suite
+ run_module_suite()
# -*- coding: utf-8 -*-
from aubio import midi2note
-from nose2.tools import params
-import unittest
+from _tools import parametrize, assert_raises
list_of_known_midis = (
( 0, 'C-1' ),
( 127, 'G9' ),
)
-class midi2note_good_values(unittest.TestCase):
+class Test_midi2note_good_values(object):
- @params(*list_of_known_midis)
+ @parametrize('midi, note', list_of_known_midis)
def test_midi2note_known_values(self, midi, note):
" known values are correctly converted "
- self.assertEqual ( midi2note(midi), note )
+ assert midi2note(midi) == (note)
-class midi2note_wrong_values(unittest.TestCase):
+class Test_midi2note_wrong_values(object):
def test_midi2note_negative_value(self):
" fails when passed a negative value "
- self.assertRaises(ValueError, midi2note, -2)
+ assert_raises(ValueError, midi2note, -2)
def test_midi2note_large(self):
" fails when passed a value greater than 127 "
- self.assertRaises(ValueError, midi2note, 128)
+ assert_raises(ValueError, midi2note, 128)
def test_midi2note_floating_value(self):
" fails when passed a floating point "
- self.assertRaises(TypeError, midi2note, 69.2)
+ assert_raises(TypeError, midi2note, 69.2)
def test_midi2note_character_value(self):
" fails when passed a value that can not be transformed to integer "
- self.assertRaises(TypeError, midi2note, "a")
+ assert_raises(TypeError, midi2note, "a")
if __name__ == '__main__':
- import nose2
- nose2.main()
+ from _tools import run_module_suite
+ run_module_suite()
#! /usr/bin/env python
-from unittest import main
import numpy as np
from numpy.testing import TestCase
-from numpy.testing.utils import assert_equal, assert_almost_equal
+from numpy.testing import assert_equal, assert_almost_equal
from aubio import window, level_lin, db_spl, silence_detection, level_detection
from aubio import fvec, float_type
assert level_detection(ones(1024, dtype = float_type), -70) == 0
if __name__ == '__main__':
+ from unittest import main
main()
from __future__ import unicode_literals
-from aubio import note2midi, freq2note
-from nose2.tools import params
-import unittest
+from aubio import note2midi, freq2note, note2freq, float_type
+from numpy.testing import TestCase
+from _tools import parametrize, assert_raises, skipTest
list_of_known_notes = (
( 'C-1', 0 ),
( '2' ),
)
-class note2midi_good_values(unittest.TestCase):
+class Test_note2midi_good_values(object):
- @params(*list_of_known_notes)
+ @parametrize('note, midi', list_of_known_notes)
def test_note2midi_known_values(self, note, midi):
" known values are correctly converted "
- self.assertEqual ( note2midi(note), midi )
+ assert note2midi(note) == midi
- @params(*list_of_known_notes_with_unicode_issues)
+ @parametrize('note, midi', list_of_known_notes_with_unicode_issues)
def test_note2midi_known_values_with_unicode_issues(self, note, midi):
- " known values are correctly converted, unless decoding is expected to fail"
+ " difficult values are correctly converted unless expected failure "
try:
- self.assertEqual ( note2midi(note), midi )
+ assert note2midi(note) == midi
except UnicodeEncodeError as e:
+ # platforms with decoding failures include:
+ # - osx: python <= 2.7.10
+ # - win: python <= 2.7.12
import sys
- strfmt = "len(u'\\U0001D12A') != 1, excpected decoding failure | {:s} | {:s} {:s}"
- strres = strfmt.format(e, sys.platform, sys.version)
- # happens with: darwin 2.7.10, windows 2.7.12
+ strmsg = "len(u'\\U0001D12A') != 1, expected decoding failure"
+ strmsg += " | upgrade to Python 3 to fix"
+ strmsg += " | {:s} | {:s} {:s}"
if len('\U0001D12A') != 1 and sys.version[0] == '2':
- self.skipTest(strres + " | upgrade to Python 3 to fix")
+ skipTest(strmsg.format(repr(e), sys.platform, sys.version))
else:
raise
-class note2midi_wrong_values(unittest.TestCase):
+class note2midi_wrong_values(TestCase):
def test_note2midi_missing_octave(self):
" fails when passed only one character"
" fails when passed a note with a note name longer than expected"
self.assertRaises(ValueError, note2midi, 'CB+-3')
- @params(*list_of_unknown_notes)
+class Test_note2midi_unknown_values(object):
+
+ @parametrize('note', list_of_unknown_notes)
def test_note2midi_unknown_values(self, note):
" unknown values throw out an error "
- self.assertRaises(ValueError, note2midi, note)
+ assert_raises(ValueError, note2midi, note)
-class freq2note_simple_test(unittest.TestCase):
+class freq2note_simple_test(TestCase):
- def test_freq2note(self):
+ def test_freq2note_above(self):
" make sure freq2note(441) == A4 "
self.assertEqual("A4", freq2note(441))
+ def test_freq2note_under(self):
+ " make sure freq2note(439) == A4 "
+ self.assertEqual("A4", freq2note(439))
+
+class note2freq_simple_test(TestCase):
+
+ def test_note2freq(self):
+ " make sure note2freq('A3') == 220"
+ self.assertEqual(220, note2freq("A3"))
+
+ def test_note2freq_under(self):
+ " make sure note2freq(A4) == 440"
+ if float_type == 'float32':
+ self.assertEqual(440, note2freq("A4"))
+ else:
+ self.assertLess(abs(note2freq("A4")-440), 1.e-12)
+
if __name__ == '__main__':
- import nose2
- nose2.main()
+ from _tools import run_module_suite
+ run_module_suite()
#! /usr/bin/env python
-from unittest import main
from numpy.testing import TestCase, assert_equal, assert_almost_equal
-from aubio import notes
+from aubio import notes, source
+import numpy as np
+from utils import list_all_sounds
+
+list_of_sounds = list_all_sounds('sounds')
AUBIO_DEFAULT_NOTES_SILENCE = -70.
+AUBIO_DEFAULT_NOTES_RELEASE_DROP = 10.
AUBIO_DEFAULT_NOTES_MINIOI_MS = 30.
class aubio_notes_default(TestCase):
self.o.set_silence(val)
assert_equal (self.o.get_silence(), val)
-from .utils import list_all_sounds
-list_of_sounds = list_all_sounds('sounds')
+ def test_get_release_drop(self):
+ assert_equal (self.o.get_release_drop(), AUBIO_DEFAULT_NOTES_RELEASE_DROP)
+
+ def test_set_release_drop(self):
+ val = 50
+ self.o.set_release_drop(val)
+ assert_equal (self.o.get_release_drop(), val)
+
+ def test_set_release_drop_wrong(self):
+ val = -10
+ with self.assertRaises(ValueError):
+ self.o.set_release_drop(val)
class aubio_notes_sinewave(TestCase):
def analyze_file(self, filepath, samplerate=0):
- from aubio import source
- import numpy as np
win_s = 512 # fft size
hop_s = 256 # hop size
assert_equal (results[0][1], [69, 123, -1])
if __name__ == '__main__':
+ from unittest import main
main()
#! /usr/bin/env python
-from unittest import main
from numpy.testing import TestCase, assert_equal, assert_almost_equal
-from aubio import onset
+from aubio import onset, fvec
class aubio_onset_default(TestCase):
class aubio_onset_8000(aubio_onset_params):
samplerate = 8000
+class aubio_onset_coverate(TestCase):
+ # extra tests to execute the C routines and improve coverage
+
+ def test_all_methods(self):
+ for method in ['default', 'energy', 'hfc', 'complexdomain', 'complex',
+ 'phase', 'wphase', 'mkl', 'kl', 'specflux', 'specdiff',
+ 'old_default']:
+ o = onset(method=method, buf_size=512, hop_size=256)
+ o(fvec(256))
+
+ def test_get_methods(self):
+ o = onset(method='default', buf_size=512, hop_size=256)
+
+ assert o.get_silence() == -70
+ o.set_silence(-20)
+ assert_almost_equal(o.get_silence(), -20)
+
+ assert o.get_compression() == 1
+ o.set_compression(.99)
+ assert_almost_equal(o.get_compression(), .99)
+
+ assert o.get_awhitening() == 0
+ o.set_awhitening(1)
+ assert o.get_awhitening() == 1
+
+ o.get_last()
+ o.get_last_ms()
+ o.get_last_s()
+ o.get_descriptor()
+ o.get_thresholded_descriptor()
+
+
if __name__ == '__main__':
+ from unittest import main
main()
#! /usr/bin/env python
from numpy.testing import TestCase, assert_equal, assert_array_less
+from _tools import parametrize
from aubio import fvec, cvec, pvoc, float_type
-from nose2 import main
-from nose2.tools import params
import numpy as np
if float_type == 'float32':
def create_noise(hop_s):
return np.random.rand(hop_s).astype(float_type) * 2. - 1.
-class aubio_pvoc_test_case(TestCase):
+class Test_aubio_pvoc_test_case(object):
""" pvoc object test case """
def test_members_automatic_sizes_default(self):
+ 'This is expected when using fftw3 on powerpc.')
assert_equal ( r, 0.)
- @params(
+ def test_no_overlap(self):
+ win_s, hop_s = 1024, 1024
+ f = pvoc (win_s, hop_s)
+ t = fvec (hop_s)
+ for _ in range(4):
+ s = f(t)
+ r = f.rdo(s)
+ assert_equal ( t, 0.)
+
+ resynth_noise_args = "hop_s, ratio"
+ resynth_noise_values = [
( 256, 8),
( 256, 4),
( 256, 2),
(8192, 8),
(8192, 4),
(8192, 2),
- )
+ ]
+
+ @parametrize(resynth_noise_args, resynth_noise_values)
def test_resynth_steps_noise(self, hop_s, ratio):
""" check the resynthesis of a random signal is correct """
sigin = create_noise(hop_s)
self.reconstruction(sigin, hop_s, ratio)
- @params(
+ resynth_sine_args = "samplerate, hop_s, ratio, freq"
+ resynth_sine_values = [
(44100, 256, 8, 441),
(44100, 256, 4, 1203),
(44100, 256, 2, 3045),
(22050, 256, 8, 445),
(96000, 1024, 8, 47000),
(96000, 1024, 8, 20),
- )
+ ]
+
+ @parametrize(resynth_sine_args, resynth_sine_values)
def test_resynth_steps_sine(self, samplerate, hop_s, ratio, freq):
""" check the resynthesis of a sine is correct """
sigin = create_sine(hop_s, freq, samplerate)
self.skipTest('creating aubio.pvoc with size %d did not fail' % win_s)
if __name__ == '__main__':
+ from unittest import main
main()
-
#! /usr/bin/env python
-from unittest import TestCase, main
-from numpy.testing import assert_equal
+from numpy.testing import TestCase, assert_equal
from numpy import sin, arange, mean, median, isnan, pi
from aubio import fvec, pitch, freqtomidi, float_type
for algo in pitch_algorithms:
for mode in signal_modes:
- test_method = create_test (algo, mode)
- test_method.__name__ = 'test_pitch_%s_%d_%d_%dHz_sin_%.0f' % ( algo,
+ _test_method = create_test (algo, mode)
+ _test_method.__name__ = 'test_pitch_%s_%d_%d_%dHz_sin_%.0f' % ( algo,
mode[0], mode[1], mode[2], mode[3] )
- setattr (aubio_pitch_Sinusoid, test_method.__name__, test_method)
+ setattr (aubio_pitch_Sinusoid, _test_method.__name__, _test_method)
if __name__ == '__main__':
+ from unittest import main
main()
#! /usr/bin/env python
-from nose2 import main
-from nose2.tools import params
from numpy.testing import TestCase
from aubio import fvec, source, sink
-from .utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path
-
-import warnings
-warnings.filterwarnings('ignore', category=UserWarning, append=True)
+from utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path
+from utils import parse_file_samplerate
+from _tools import parametrize, skipTest, assert_raises, assert_warns
list_of_sounds = list_all_sounds('sounds')
samplerates = [0, 44100, 8000, 32000]
for samplerate in samplerates:
all_params.append((hop_size, samplerate, soundfile))
-class aubio_sink_test_case(TestCase):
-
- def setUp(self):
- if not len(list_of_sounds):
- self.skipTest('add some sound files in \'python/tests/sounds\'')
+class Test_aubio_sink(object):
def test_wrong_filename(self):
- with self.assertRaises(RuntimeError):
+ with assert_raises(RuntimeError):
sink('')
def test_wrong_samplerate(self):
- with self.assertRaises(RuntimeError):
+ with assert_raises(RuntimeError):
sink(get_tmp_sink_path(), -1)
def test_wrong_samplerate_too_large(self):
- with self.assertRaises(RuntimeError):
+ with assert_raises(RuntimeError):
sink(get_tmp_sink_path(), 1536001, 2)
def test_wrong_channels(self):
- with self.assertRaises(RuntimeError):
+ with assert_raises(RuntimeError):
sink(get_tmp_sink_path(), 44100, -1)
def test_wrong_channels_too_large(self):
- with self.assertRaises(RuntimeError):
+ with assert_raises(RuntimeError):
sink(get_tmp_sink_path(), 44100, 202020)
def test_many_sinks(self):
g.close()
shutil.rmtree(tmpdir)
- @params(*all_params)
+ @parametrize('hop_size, samplerate, path', all_params)
def test_read_and_write(self, hop_size, samplerate, path):
-
+ orig_samplerate = parse_file_samplerate(soundfile)
try:
- f = source(path, samplerate, hop_size)
+ if orig_samplerate is not None and orig_samplerate < samplerate:
+ # upsampling should emit a warning
+ with assert_warns(UserWarning):
+ f = source(soundfile, samplerate, hop_size)
+ else:
+ f = source(soundfile, samplerate, hop_size)
except RuntimeError as e:
- self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e)))
+ err_msg = '{:s} (hop_s = {:d}, samplerate = {:d})'
+ skipTest(err_msg.format(str(e), hop_size, samplerate))
if samplerate == 0: samplerate = f.samplerate
sink_path = get_tmp_sink_path()
g = sink(sink_path, samplerate)
if read < f.hop_size: break
del_tmp_sink_path(sink_path)
- @params(*all_params)
+ @parametrize('hop_size, samplerate, path', all_params)
def test_read_and_write_multi(self, hop_size, samplerate, path):
+ orig_samplerate = parse_file_samplerate(soundfile)
try:
- f = source(path, samplerate, hop_size)
+ if orig_samplerate is not None and orig_samplerate < samplerate:
+ # upsampling should emit a warning
+ with assert_warns(UserWarning):
+ f = source(soundfile, samplerate, hop_size)
+ else:
+ f = source(soundfile, samplerate, hop_size)
except RuntimeError as e:
- self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e)))
+ err_msg = '{:s} (hop_s = {:d}, samplerate = {:d})'
+ skipTest(err_msg.format(str(e), hop_size, samplerate))
if samplerate == 0: samplerate = f.samplerate
sink_path = get_tmp_sink_path()
g = sink(sink_path, samplerate, channels = f.channels)
g(vec, 128)
if __name__ == '__main__':
- main()
+ from _tools import run_module_suite
+ run_module_suite()
#! /usr/bin/env python
-from unittest import main
from numpy.testing import TestCase, assert_equal
from aubio import slice_source_at_stamps
-from .utils import count_files_in_directory, get_default_test_sound
-from .utils import count_samples_in_directory, count_samples_in_file
+from utils import count_files_in_directory, get_default_test_sound
+from utils import count_samples_in_directory, count_samples_in_file
import tempfile
import shutil
def test_slice_start_only_no_zero(self):
regions_start = [i*1000 for i in range(1, n_slices)]
- slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir)
+ slice_source_at_stamps(self.source_file, regions_start,
+ output_dir = self.output_dir, create_first=True)
def test_slice_start_beyond_end(self):
regions_start = [i*1000 for i in range(1, n_slices)]
regions_start += [count_samples_in_file(self.source_file) + 1000]
- slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir)
+ slice_source_at_stamps(self.source_file, regions_start,
+ output_dir = self.output_dir, create_first=True)
def test_slice_start_every_blocksize(self):
hopsize = 200
- regions_start = [i*hopsize for i in range(1, n_slices)]
+ regions_start = [i*hopsize for i in range(0, n_slices)]
slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir,
hopsize = 200)
+ def test_slice_start_every_half_blocksize(self):
+ hopsize = 200
+ regions_start = [i*hopsize//2 for i in range(0, n_slices)]
+ slice_source_at_stamps(self.source_file, regions_start,
+ output_dir = self.output_dir, hopsize = 200)
+
def tearDown(self):
original_samples = count_samples_in_file(self.source_file)
written_samples = count_samples_in_directory(self.output_dir)