From: Paul Brossier Date: Fri, 10 Mar 2017 13:26:32 +0000 (+0100) Subject: Merge branch 'master' into awhitening X-Git-Tag: 0.4.5~20^2~37 X-Git-Url: https://git.aubio.org/?p=aubio.git;a=commitdiff_plain;h=155cc10b5cde76184291c01fe8723328f0e36430;hp=00d0275bc3279e523ce21d183884d24024dfbff8 Merge branch 'master' into awhitening --- diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..9e247cf2 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,69 @@ +# appveyor configuration. See http://www.appveyor.com/docs/appveyor-yml +# and http://www.appveyor.com/docs/installed-software#python + +environment: + + matrix: + + # pre-installed python version, see: + # http://www.appveyor.com/docs/installed-software#python + - PYTHON: "C:\\Python27" + PYTHON_VERSION: "2.7.x" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python27-x64" + PYTHON_VERSION: "2.7.x" + PYTHON_ARCH: "64" + + - PYTHON: "C:\\Python34" + PYTHON_VERSION: "3.4.x" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python34-x64" + PYTHON_VERSION: "3.4.x" + PYTHON_ARCH: "64" + + - PYTHON: "C:\\Python35" + PYTHON_VERSION: "3.5.x" + PYTHON_ARCH: "32" + + - PYTHON: "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" + +install: + + - ECHO "Installed SDKs:" + - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\"" + + # Check that we have the expected version and architecture for Python + - "%PYTHON%\\python.exe --version" + - "%PYTHON%\\python.exe -c \"import struct; print(struct.calcsize('P') * 8)\"" + + # We need wheel installed to build wheels + - "%PYTHON%\\python.exe -m pip install wheel" + + - "SET PATH=%PATH_EXTRAS%;%PYTHON%;%PYTHON%\\Scripts;%PATH%" + + - "pip install --disable-pip-version-check --user --upgrade pip" + - "pip install --upgrade setuptools" + +before_build: + - curl -fsS -o waf https://waf.io/waf-1.8.22 + - curl -fsS -o waf.bat https://raw.githubusercontent.com/waf-project/waf/master/utils/waf.bat + +build_script: + # build python module without using libaubio + - "pip install -r requirements.txt" + - "python setup.py build" + - "pip install ." + - "python python\\demos\\demo_create_test_sounds.py" + - "nose2 --verbose" + # clean up + - waf distclean + # build libaubio + - waf configure build --verbose + # build python module using libaubio dll + - "python setup.py build" diff --git a/.gitignore b/.gitignore index f87e6290..f0d0dcac 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ build/ # doxygen doc/web/ doc/full/ +doc/_build/ python/gen python/dist @@ -39,3 +40,7 @@ python/*.wav aubio-*.tar.bz2 aubio-*.zip + +# test sounds +python/tests/sounds +aubio.egg-info diff --git a/.landscape.yml b/.landscape.yml new file mode 100644 index 00000000..d180360c --- /dev/null +++ b/.landscape.yml @@ -0,0 +1,5 @@ +strictness: medium +test-warnings: true +python-targets: + - 2 + - 3 diff --git a/.travis.yml b/.travis.yml index dc50e8a7..73aef644 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,121 @@ -language: c +language: python -sudo: false +matrix: + include: + - python: 3.5 + os: linux + compiler: gcc + - python: 3.4 + os: linux + compiler: gcc + - python: 2.7 + os: linux + compiler: gcc + - language: C + os: osx + osx_image: xcode8 + compiler: clang + - python: 3.5 + os: linux + compiler: gcc + env: CFLAGS="-Os" WAFOPTS="--disable-samplerate --disable-sndfile" + - python: 3.4 + os: linux + compiler: gcc + env: HAVE_AUBIO_DOUBLE=1 CFLAGS="-O3" WAFOPTS="--enable-fftw3" + - python: 2.7 + os: linux + compiler: gcc + 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 + - 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 -compiler: - - gcc - - clang - -env: - - ARCH=i386 - - ARCH=x86_64 +# use trusty +dist: trusty +sudo: required addons: apt: packages: - bzip2 + - libavcodec-dev + - libavformat-dev + - libavresample-dev + - libavutil-dev - libsndfile1-dev - libsamplerate-dev - libjack-dev - libasound2-dev - libfftw3-dev - - python-dev - - python-numpy + - sox + +before_install: + - | + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update + brew install sox + brew install ffmpeg + brew install libsndfile + export PATH="$HOME/Library/Python/2.7/bin/:$PATH" + fi; + +install: + - travis_retry pip install --upgrade pip + - travis_retry make getwaf expandwaf deps_python + - which pip + - pip --version script: - - make build - - make build_python - - make clean_python - - make clean - - make distcheck + - make create_test_sounds + - | + if [[ -z "$AUBIO_NOTESTS" ]]; then + make test_lib_python_clean + make test_python_only_clean + else + make test_lib_only_clean + fi; + +notifications: + irc: + channels: + - "irc.freenode.org#aubio" + use_notice: true + webhooks: + urls: + - https://webhooks.gitter.im/e/81e7733a5b1d977854b4 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/ChangeLog b/ChangeLog index ba1d3334..d2b29d33 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,450 @@ +2017-01-08 Paul Brossier + + [ Overview ] + + * VERSION: bump to 0.4.4 + * src/utils/log.h: new function to redirect log, error, and warnings + * python/: AUBIO_ERR raises python exception, AUBIO_WRN to emit py warning + * doc/: add some documentation, fix errors in manpages + * wscript: new rules to build 'manpages', 'doxygen', and 'sphinx', new + --build-type= option (thanks to Eduard Mueller) + * src/notes/notes.h: add minioi and silence methods + * examples/: add --minioi (minimum inter-onset interval) option + * src/pitch/pitchyin.c: improve msvc compiler optimisations (thanks to + Eduard Mueller) + * python/, src/: improve error messages, fix minor memory leaks + * src/io/source_avcodec.c: improve compatibility with latest ffmpeg and with + older libav/ffmpeg versions + * python/demos/: new demos to capture microphone in real time + + [ Interface] + + * src/aubio.h: include utils/log.h + * src/utils/log.h: add new aubio_log_set_function to redirect log messages + * src/notes/notes.h: add aubio_notes_{get,set}_minioi_ms, add + _{get,set}_silence methods + + [ Library ] + + * src/aubio_priv.h: add AUBIO_INF to print to stdout with header, use new + logging function, add ATAN alias, add stdarg.h, move #include "config.h" + * src/{fmat,fvec}.c: avoid integer division + * src/pitch/pitchyin.c: [msvc] help compiler to optimize aubio_pitchyin_do + by giving it addresses for all arrays which are referenced in inner loops, + thanks to Eduard Mueller. + * src/pitch/pitch.c: declare internal functions as static, fail on wrong + method, warn on wrong unit, improve error messages, fix error string + * src/spectral/specdesc.c: return NULL if wrong mode asked, remove trailing + spaces + * src/onset/onset.c: return null and clean-up if new_aubio_specdesc failed, + fix error message + * src/notes/notes.c: use midi note to store pitch candidate, round to + nearest note, add a variable to define precision, fix out-of-bound write, + fix unset silence_threshold, fix error message + * src/spectral/ooura_fft8g.c: add cast to avoid conversion warnings, prefix + public function with aubio_ooura_ to avoid with other apps using ooura (e.g. + puredata), make internal functions static, + * src/spectral/fft.c: add message about fftw3 being able to do non-power of + two sizes, make calls to fftw_destroy_plan thread-safe, use prefixed + aubio_ooura_rdft + * src/spectral/phasevoc.c: fix error string + * src/temporal/resampler.c: throw an error when using libsamplerate with doubles + * src/io/ioutils.h: add functions to check samplerate and channels, use in sink_*.c + * src/io/source.c: add error message when aubio was compiled with no source, + only show error message from last child source_ + * src/io/source_avcodec.c: call avformat_free_context after + avformat_close_input, keep a reference to packet to remove it when closing + file, avoid deprecation warnings with ffmpeg 3.2, add backward compatibility + for libavcodec55, fix for old libavcodec54, use AV_SAMPLE_FMT_DBL when + compiling with HAVE_AUBIO_DOUBLE, fix missing samples in eof block, avoid + function calls before declarations, improve error messages, replace with new + context before closing old one, make sure s->path is set to null + * src/io/{source_wavread,sink_wavwrite}.c: declare internal functions as static + * src/io/source_wavread.c: fix bytes_read for JUNK headers, improve error + messages, initialize buffer, skip chunks until data is found, or abort, skip + junk chunk + * src/io/source_sndfile.c: add support for multi-channel resampling, set + handle to null after sucessful close, add missing floor in ratio comparison, + improve formatting + * src/io/sink.c: only show error message from last child sink_ + * src/io/sink_apple_audio.c: avoid crash on empty file name + * src/io/sink_sndfile.c: improve error message + * src/io/sink_{sndfile,wavwrite}.c: use AUBIO_MAX_CHANNELS, fix error message + + [ Documentation ] + + * README.md: update copyright dates, use https + * src/aubio.h: add some links to examples, use https + * src/pitch/pitch.h: add aubio_pitch_get_tolerance, add basic description of + unit modes + * src/notes/notes.h: add doxygen header + * src/spectral/fft.h: strip example path + * doc/*.rst: improve sphinx documentation + * doc/android.rst: add reference to it scripts/build_android + * doc/debian_packages.rst: added page on debian packages + * doc/python_module.rst: add demo_source_simple.py, add note on pip, add + `print(aubio.version)` + * doc/cli.rst: include command line manpages + * doc/cli_features.rst: add matrix of command line features + * doc/requirements.rst: add a note about --notests (closes #77), document + --msvc options, improve description of options + * doc/download.rst: added page on download + * doc/installing.rst: update + * doc/xcode_frameworks.rst: added page on xcode frameworks + * doc/**: use https://aubio.org + * doc/conf.py: use pyramid theme, update copyright, remove hardcoded path + * doc/web.cfg: exclude ioutils from doc + * doc/aubionotes.txt: document -M option (see #18), + * doc/aubioonset.txt: add documentation for -M, --minioi, improve threshold + description (thanks to Peter Parker), fix typo (onset, not pitch) + * doc/aubio*.txt: document -T/--timeformat option + + [ Build ] + + * Makefile: add a brief intro, avoid offline operations, add html and dist + targets, add rules for documentation, simplify listing, avoid offline + operations, bump waf to 1.9.6, check for waf before clean, chmod go-w + waflib, improve clean, use pip to install, factorise pip options, generate + more test sounds, improve test_python and test_pure_python, pass build_ext + in test_pure_python{,_wheel}, quieten uninstall_python if already + uninstalled, improve test targets, use bdist_wheel in test_pure_python, + build_ext only for --enable-double, verbose waf rules, add cleanwaf + * wscript: added debug/release build type configurations release (default) + enables optimizations, debug symbols are enabled in both configurations, + thanks to Eduard Mueller. + * wscript: add options to disable source_wavread/sink_wavwrite, add check + for stdarg.h, new rules 'manpages', 'sphinx', and 'doxygen' to build + documentation, add version to sphinx and manpages, disable libsamplerate + if double precision enabled (libsamplerate only supports float), fix typos, + remove trailing spaces, improve tarball creation (./waf dist), remove + full.cfg from tarball, prepend to CFLAGS to honor user cflags + * wscript, src/wscript_build: improve install locations using DATAROOTDIR, + MANDIR, INCLUDEDIR + * wscript: default to no atlas for now + * src/wscript_build: always build static library + * scripts/build_android: add an example script to build aubio on android, + + [ Tools ] + + * examples/aubionotes.c: use new notes, set minioi, send last note off when + needed, add warning for missing options + * examples/aubioonset.c: add minioi option, in seconds + * examples/: only send a last note off when using jack + * examples/: return 1 if object creation failed + * examples/: use PROG_HAS_OUTPUT, add PROG_HAS_SILENCE + + [ Tests ] + + * tests/src/spectral/test-fft.c: fix default size + * tests/src/spectral/test-phasevoc.c: fix typos + * tests/src/utils/test-log.c: add AUBIO_INF, add example for + aubio_log_set_function, improve messages + + [ Python ] + + * python/ext/aubiomodule.c: add aubio._aubio.__version__ and import it as + aubio.version, use custom logging function for errors and warnings, remove + duplicated add_generated_objects, use <> for non local aubio + * python/ext/py-cvec.c: use NPY_INTP_FMT + * python/ext/py-fft.c: use error string set in src/spectral/fft.c + * python/ext/py-phasevoc.c: use error string set in src/spectral/phasevoc.c + * python/ext/py-sink.c: always set samplerate and channels in init + * python/ext/py-source.c: use error string set in src/io/source.c + * python/lib/aubio/midiconv.py: add unicode double sharp and double flat, + improve unicode handling, skip UnicodeEncodeError on python 2.x + + [ Python build ] + + * MANIFEST.in: add src/**.c, exclude full.cfg, include waflib, remove + python/ext/config.h + * setup.py: define AUBIO_VERSION use sorted glob.glob to improve + reproducibility, remove extra quotes, remove status from version string, + update description, use custom build_ext instead of 'generate' command, + define HAVE_AUBIO_DOUBLE to 1 if needed + * python/lib/gen_code.py: add support for multiple _do outputs, fix number + of output, improve del_ function, safer DECREF, fix indentation, emit RuntimeError + * python/lib/gen_external.py: clean-up, enable tss, remove duplicate, + sort generated files + * python/lib/moresetuptools.py: add HAVE_STDARG_H, also check for + HAVE_AUBIO_DOUBLE, cleaner clean, look first for system library, then for + local build, then local sources, mo nore fake config.h here, use + samplerate in single precision only + * python/README.md: add a note about nose2 for python tests (closes #74) + * scripts/setenv_local.sh: python3 compat + + [ Python demos ] + + * python/demos/demo_alsa.py: add example using alsaaudio (closes #72) + * python/demos/demo_mfcc.py: add options to plot first and second + derivatives, and set samplerate/win_s/hop_s, thanks to @jhoelzl (closes #68) + * python/demos/demo_notes.py: add simple notes demos + * python/demos/demo_pyaudio.py: added simple demo for pyaudio, see #6, + closes #78, thanks to @jhoelzl and @notalentgeek, add some comments, avoid + overwriting aubio.pitch + * python/demos/demo_source_simple.py: fix indentation, make executable + * python/demos/demo_timestretch{,_online}.py: fix usage string, remove + unused import, use // to yield an integer (closes #71) + * python/demos/demo_timestretch_online.py: use 512, fix block counter + * python/demos/demo_tss.py: improve default parameters, exit before plotting + + [ Python tests ] + + * python/tests/: use local import, add __init__.py + * python/tests/test_cvec.py: simplify + * python/tests/test_fft.py: skip test fft(zeros).phas == 0 if needed, expected powerpc + * python/tests/test_fvec.py: reduce alpha norm precision to 10.-4 + * python/tests/test_{midi2note,note2midi}.py: use nose2.params, add unicode tests + * python/tests/test_notes.py: add basic tests + * python/tests/test_notes.py: test results are correct for 44100Hz_44100f_sine441.wav + * python/tests/test_sink.py: add more tests, quiet warnings + * python/tests/test_source.py: break long line, check the tail of the file + is non-zero on non silent test files, filter user warnings to avoid spamming + the console, only check if last frames are non silent on brownnoise (weak), + remove fragile brownnoise test, check duration on short files, use nose2 + params to process one sound file per test + * python/tests/test_specdesc.py: RuntimeError is now raised on wrong mode + * python/tests/utils.py: by default, use 5 seconds brownoise + + [ Only in git ] + + * .travis.yml: add debian dpkg-buildflags config, switch from precise to + trusty, sudo required, add ffmpeg on osx, add targets ios, iosimulator, + and osx noopt configs, bump to xcode8, add xcode8.2 config, mimick + build_apple_frameworks options, alway upgrade pip, add pip --version and + which pip after upgrading, remove --user, use expandwaf in install, remove + unused ARCH, shuffle order, remove duplicate, add missing opening quote, + use AUBIO_NOTESTS to build only lib on ios, add gitter webhook + * .appveyor.yml: fix path for windows+python 3.5, fix typo in path, make + nose2 tests verbose + +2016-08-16 Paul Brossier + + [ Interface ] + + * src/io/source.h, src/io/source_*.h: add _get_duration + * src/notes/notes.h: add basic notes object + * src/tempo/beattracking.{c,h}: add _get_period and _get_period_s + * src/mathutils.h: add fvec_ishift + * src/fvec.{c,h}: add fvec_weighted_copy + * src/tempo/tempo.{c,h}: add _get_period and _get_period_s, also add tatum, + a subdivision of the beat period, default to 4, implement get/set_delay + * src/**.{c,h}: use #ifdef HAVE_FOO, not #if _HAVE_FOO, add const qualifiers + to unmodified pointers (see #35) + + [ Library ] + + * src/{fmat,fvec,mathutils}.c: optimisations (using atlas or Accelerate when + available) for fvec_sum, fvec_mean, fvec_shift, aubio_level_lin, + fvec_set_all, fvec_zeros, fvec_weight, fvec_copy, fvec_weighted_copy, + fmat_vecmul + * src/aubio_priv.h: check for atlas cblas, use cblas_xswap, vDSP_dotpr, + protect SQR parameters, avoid redefining MIN/MAX, define PATH_MAX and PI + when needed, use _isnan on windows msvc 9 to avoid linking error, more + windows hacks + * src/mathutils.c: avoid for loop initial declarations [gcc], use + HAVE_ATLAS, use smpl_t for constants + * src/fmat.c: skip asserts + * src/spectral/{filterbank,mfcc}.c: use accelerated fmat_vecmul + * src/spectral/fft.c: fftw can be used odd length sizes, not Ooura, + factorise double / single flags, use memcpy + * src/spectral/phasevoc.c: fix arguments checks, return NULL when fft + creation failed , apply windowing for resynthesized grain, use ishift for + odd windows, fix scaling factors for correct reconstruction at 50 and 75% + overlap + * src/pitch/pitch.c: allow for silence == 0, improve error messages + * src/pitch/pitchmcomb.c: fix candidates sorting function, really comparing + current to next + * src/notes/notes.c: equivalent to previous examples/aubionotes.c results + * src/onset/onset.c: simplify selection of first onset, fix for "conversion + from 'smpl_t' to 'uint_t', possible loss of data" with msvc + * src/pitch/pitchmcomb.c: scan across all spectrum + * src/pitch/pitchyinfft.c: use fvec_weighted_copy + * src/{spectral/*.c,onset/*.c,tempo/*.c}: make sure win_size > 1 + * src/io/*.c: use custom defines for {source,sink}_apple_audio, take a copy + of const char* path + * src/io/source_avcodec.c: + - update to libav10, libavcodec 55.34.1 + - avoid deprecation warning, detect if we use ffmpeg or libav version + - check if the uri is a network stream using av_url_split, call + avformat_network_init() if needed + - check if we still need max_analyze_duration2 (closes #53, thanks to + @anthonylauzon) + * src/io/source_{avcodec,sndfile}.c: avoid modifying input param + * src/io/{sink,source,utils}_apple_audio.c: fix memory leak calling + CFRelease (closes #26, closes #27, and closes #28) + * src/io/sink_apple_audio.c: disable async mode for now, factorise code + * src/io/source_apple_audio.c: check out of bounds _seek, set s->path, quiet + * src/io/source_sndfile.c: fix crash, zero-pad output vector when + upsampling, use sf_read_double when compiling with AUBIO_DOUBLE, approximate + duration when resampling + * src/io/sink_sndfile.c: fix for double precision + * src/synth/sampler.c: fix typo, keeps a copy of uri + * src/tempo/tempo.c: do not write novelty function in output[1] + * src/temporal/resampler.c: make msvc happier adding a dummy variable + * src/temporal/filter.c: check parameters, fix filter_do_outplace to really + avoid modifying input + * src/utils/windll.c: add dll main entry point + + [ Python ] + + * General: + - new build system, new code generator + - Python 3 compatibility (#33), thanks to Nils Philippsen (@nphilipp) + - double precision compatibility + - simplify memory allocations, removed unneeded malloc/free calls + - fix memory leak (#49), check input sizes (#63) and output sizes (#60) + - improve indentation, clean up unused imports and variables + - fix comparison to None and to False + * setup.py: move from python/setup.py, add option to build libaubio inside + python-aubio (for instance with pip), add command 'generate' with option + '--enable-double', build with -Wdeclaration-after-statement -Werror + * python/ext/aubiomodule.c: fix PyMethodDef sentinel + * python/ext/aubioproxy.c: factorize input checks into + PyAubio_IsValidVector, fix windows c89 compilation, use npy_intp, not long + * python/ext: rewrite and simplify, safer and improved memory usage (#49), + improve error strings, verify actual object creation + * python/ext/py-source.c: added duration, check seek is not negative + * python/ext/py-musicutils.c: do not overwrite PyArg_ParseTuple messages + * python/lib/gen_code.py: new generator, switch to using custom PyObjects + instead of fvec, cvec, fmat, ready for double precision (defaults to single) + * python/lib/aubio__init__.py: use new aubio.float_type, make sure length is + not zero and float_type is imported + * python/lib/aubio/midiconv.py: fix instance checks, make sure midi2note + uses midi int (#33) + * python/lib/aubio/slicing.py: fix samplerate + * python/ext/aubio-types.h: add new_py_ functions to create PyObjects + instead of fvec_t, apply to generated and hard-coded objects + * python/lib/gen_external.py: improve compiler detection, fixes build on + windows (#55) + * python/lib/moresetuptools.py: helpers for windows and macos compilations + + [ Python demos ] + + * python/demos/demo_reading_speed.py: new reading speed tests, external + packages disabled by default + * python/demos/demo_timestretch.py: new timescale algorithm + * python/demos/demo_timestretch_online.py: new timescale algorithm (online + version) + * python/demos/demo_create_test_sounds.py: add script to create simple sound + files to test on using sox + * python/demos/demo_a_weighting.py: add simple demo for a_weighting + * python/demos/demo_filter.py: moved from _a_weighting + * python/demos/demo_mfcc.py: use n_coeffs + * python/demos/demo_bpm_extract.py: add exception type, avoid {} as default + argument value + * python/demos/demo_pysoundcard_*: update to pysoundcard 0.5.2 (closes #42) + * python/scripts/aubiocut: fix usage string output + + [ Python tests ] + + * python/tests/run_all_tests,*.py: switch to nose2, fix most prospect warnings + * python/tests/test_fvec.py: add test_pass_to_numpy, cope with accumulated + errors + * python/tests/test_cvec.py: simplify, add more tests + * python/tests/test_fft.py: more tests, fft.do to clash on wrong size + inputs, f.rdo input size, cvec is large enough, memory tests, avoid + VisibleDeprecationWarning + * python/tests/test_filterbank.py: check for wrong values, ValueError raised + * python/tests/test_filter.py: add tests + * python/tests/test_musicutils.py: simplify, check TypeError is raised + * python/tests/test_mfcc.py: more tests, check for wrong input size (see #63) + * python/tests/test_mathutils: fix test_miditobin test, can also raise + NotImplementedError (darwin) + * python/tests/test_note2midi.py: more tests, use unicode_literals + * python/tests/test_phasevoc.py: add a note about ocasional crash check + perfect reconstruction for overlap > 75% add 50% overlap test, fix duplicate + test name, add wrong sized input tests + * python/tests/test_sink.py: remove useless many_sinks_not_closed and cruft + * python/tests/test_source.py: simplify, quieten, skip tests if no test sounds + * python/tests/test_specdesc.py: check for wrong values, skip wrong name + test, use correct input size (see #63) + * python/tests/utils.py: try reopening the file is deleting it fails on windows + * python/VERSION: removed, use same VERSION file for libaubio and python-aubio + * MANIFEST.in: move from python/, update contents + * nose2.cfg: add minimal config, set multiprocess always-on=false (fixes + coverage, pass -N to speed up the tests) + + [ Tools ] + + * examples/*.c: add time format option + * examples/{aubioonset,aubiotrack}.c: also emit midi note, thanks to + @topas-rec (closes #62) + * examples/: use outmsg to print notes (fixes #8) + * examples/aubionotes.c: use new aubio_notes object + * examples/aubiotrack.c: enable -O and -t options, fix is_beat/is_silence + types + * examples/{parse_args,utils}.h: check in config.h if getopt.h was found, or + build without for msvc, more windows hacks + * examples/utils.c: change send_noteon to accept floating point midi note number + + [ Tests ] + + * tests/src/io/test-source_apple_audio.c: shorten long line + * tests/src/io/test-source_avcodec.c: use HAVE_LIBAV, closes #10 + * tests/src/temporal: avoid crash, clarify + * tests/src/tempo/test-tempo.c: tempo back to only one output + * tests/src/test-delnull.c: improve test, avoid segfaults + * tests/src/test-lvec.c: use AUBIO_LSMP_FMT + * tests/utils_tests.h: add VA_ARGS versions of variadic macros + * tests/utils_tests.h: also use custom srandom/random when compiling with + -std=c99 + * tests/utils_tests.h: make sure M_PI and RAND_MAX are defined + + [ Build ] + + * Makefile: set waf to 1.8.22 for now, new targets create_test_sounds, + build_python, test_python, clean_python, build_python3, clean_python3, + test_pure_python, test_pure_python_wheel, (use test_pure_* targets to build + without libaubio), use 'HAVE_DOUBLE=1 make' to build in double precision + * scripts/build_apple_frameworks: add script to build macosx and ios + frameworks (see #34, #43) + * scripts/build_emscripten: add script to build with emcc and co + * scripts/build_mingw: add script to cross-compile using mingw + * scripts/get_waf.sh: added simple script to fetch latest waf + * scripts/setenv_local.sh: set environment to run from built source tree + * scripts/setenv_local.sh: update to new python-aubio build location + * tests/wscript_build: do not install test programs + * tests/wscript_build, src/wscript_build: use 'use =', simplify + * src/wscript_build: enable shared lib on ios, static lib on windows + * wscript: + - update --enable-foo to fail if foo is not found + - add -mmacosx-version-min=10.4 on darwin + - add '-fembed-bitcode' on ios (closes #31), min to 6.1 + - make fat build, add option to not build with Accelerate framework + - add option to not build with CoreAudio/AudioToolbox + - add --disable-docs option + - add -lm detection + - pass HAVE_AUBIO_DOUBLE in compiler arguments + - first check for headers, make getopt.h and unistd.h optional + - check HAVE_AV* from ctx.env + - make msvc compiler quieter, add /MD and /D_CRT_SECURE_NO_WARNINGS + - check if we find atlas/cblas.h + - new build platform emscripten + - more cleanups and updates + + [ Only in git ] + + * .travis.yml: config for https://travis-ci.org/aubio/aubio + * .appveyor.yml: config for https://ci.appveyor.com/project/piem/aubio + * .landscape.yml: config for https://landscape.io/github/aubio/aubio + * conda recipes: see https://github.com/conda/conda-recipes#387 + * .gitignore: add python/tests/sounds and .egg-info + + [ General ] + + * src/: remove trailing spaces, improve doxygen strings, update copyrights, + fix typos + * src/onset/onset.h: fix description of get/set_delay functions + * src/spectral/mfcc.h: add link to reference implementation + * src/spectral/filterbank_mel.h: update reference url + * src/musicutils.h: update link to Bernardini's paper, improve doc + * doc/aubiomfcc.txt: add a note about the output + * doc/*.cfg: update to Doxygen 1.8.8 + * python/README.md: fix typo (thanks to Sam Alexander), document how to + build in a virtualenv (see #2) + * README.md: minor updates, link to python/README.md, switch to https + * VERSION: bump to 0.4.3 + 2015-08-01 Paul Brossier [ Interface ] diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..c39c6d3e --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,27 @@ +include AUTHORS COPYING README.md VERSION ChangeLog +include python/README.md +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 +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 +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 +include scripts/* scripts/apple/Info.plist scripts/apple/Modules/module.modulemap +exclude python/gen/* diff --git a/Makefile b/Makefile index 125ec28b..d51516ac 100644 --- a/Makefile +++ b/Makefile @@ -1,31 +1,249 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# +# This makefile contains simple rules to prepare, compile, test, and install +# aubio. Try one of the following rules: +# +# $ make configure +# $ make build +# $ make install +# $ make test_python + +WAFCMD=python waf +WAFURL=https://waf.io/waf-1.9.6 + +#WAFOPTS:= +# turn on verbose mode +WAFOPTS += --verbose +# build wafopts +WAFOPTS += --destdir $(DESTDIR) +# multiple jobs +WAFOPTS += --jobs 4 +# if HAVE_AUBIO_DOUBLE is defined, pass --enable-double to waf +# python/lib/moresetuptools.py also checks for HAVE_AUBIO_DOUBLE +WAFOPTS += $(shell [ -z $(HAVE_AUBIO_DOUBLE) ] || echo --enable-double ) + +PIPOPTS += --verbose + +DESTDIR:=$(PWD)/build/dist +PYDESTDIR:=$(PWD)/build/pydist + +# default install locations +PREFIX?=/usr/local +EXEC_PREFIX?=$(PREFIX) +LIBDIR?=$(PREFIX)/lib +INCLUDEDIR?=$(PREFIX)/include +DATAROOTDIR?=$(PREFIX)/share +MANDIR?=$(DATAROOTDIR)/man + +SOX=sox + +TESTSOUNDS := python/tests/sounds + all: build checkwaf: @[ -f waf ] || make getwaf getwaf: - curl https://waf.io/waf-1.8.14 > waf - @[ -d wafilb ] || rm -fr waflib - @chmod +x waf && ./waf --help > /dev/null - @mv .waf*/waflib . && rm -fr .waf* - @sed '/^#==>$$/,$$d' waf > waf2 && mv waf2 waf - @chmod +x waf + ./scripts/get_waf.sh + +expandwaf: getwaf + [ -d wafilb ] || rm -fr waflib + $(WAFCMD) --help > /dev/null + mv .waf*/waflib . && rm -fr .waf* + sed '/^#==>$$/,$$d' waf > waf2 && mv waf2 waf + chmod +x waf && chmod -R go-w waflib + +cleanwaf: + rm -rf waf waflib .waf* + +configure: checkwaf + $(WAFCMD) configure $(WAFOPTS) + +build: configure + $(WAFCMD) build $(WAFOPTS) -build: checkwaf - ./waf configure - ./waf build +install: + # install + $(WAFCMD) install $(WAFOPTS) + +list_installed: + find $(DESTDIR) -ls | sed 's|$(DESTDIR)|/«destdir»|' + +list_installed_python: + pip show -f aubio + +list_all_installed: list_installed list_installed_python + +uninstall: + # uninstall + $(WAFCMD) uninstall $(WAFOPTS) + +delete_install: + rm -rf $(PWD)/dist/test build_python: - cd python && ./setup.py build + # build python-aubio, using locally built libaubio if found + python ./setup.py build + +build_python_extlib: + # build python-aubio using (locally) installed libaubio + [ -f $(DESTDIR)/$(INCLUDEDIR)/aubio/aubio.h ] + [ -d $(DESTDIR)/$(LIBDIR) ] + [ -f $(DESTDIR)/$(LIBDIR)/pkgconfig/aubio.pc ] + PKG_CONFIG_PATH=$(DESTDIR)/$(LIBDIR)/pkgconfig \ + CFLAGS="-I$(DESTDIR)/$(INCLUDEDIR)" \ + LDFLAGS="-L$(DESTDIR)/$(LIBDIR)" \ + make build_python + +deps_python: + # install or upgrade python requirements + pip install $(PIPOPTS) --requirement requirements.txt + +# use pip or distutils? +install_python: install_python_with_pip +uninstall_python: uninstall_python_with_pip +#install_python: install_python_with_distutils +#uninstall_python: uninstall_python_with_distutils + +install_python_with_pip: + # install package + pip install $(PIPOPTS) . + +uninstall_python_with_pip: + # uninstall package + ( pip show aubio | grep -l aubio > /dev/null ) && \ + pip uninstall -y -v aubio || echo "info: aubio package is not installed" + +install_python_with_distutils: + ./setup.py install $(PIPOPTS) $(DISTUTILSOPTS) + +uninstall_python_with_distutils: + #./setup.py uninstall + [ -d $(PYDESTDIR)/$(LIBDIR) ] && echo Warning: did not clean $(PYDESTDIR)/$(LIBDIR) || true + +force_uninstall_python: + # ignore failure if not installed + -make uninstall_python + +local_dylib: + # DYLD_LIBRARY_PATH is no more on mac os + # create links from ~/lib/lib* to build/src/lib* + [ -f $(PWD)/build/src/libaubio.[0-9].dylib ] && ( mkdir -p ~/lib && cp -prv build/src/libaubio.[0-9].dylib ~/lib ) || true + +test_python: export LD_LIBRARY_PATH=$(DESTDIR)/$(LIBDIR) +test_python: export PYTHONPATH=$(PYDESTDIR)/$(LIBDIR) +test_python: local_dylib + # run test with installed package + ./python/tests/run_all_tests --verbose + # also run with nose, multiple processes + nose2 -N 4 clean_python: - cd python && ./setup.py clean + ./setup.py clean + +check_clean_python: + # check cleaning a second time works + make clean_python + make clean_python + +clean: checkwaf + # optionnaly clean before build + -$(WAFCMD) clean + # remove possible left overs + -rm -rf doc/_build -clean: - ./waf clean +check_clean: + # check cleaning after build works + $(WAFCMD) clean + # check cleaning a second time works + $(WAFCMD) clean -distcheck: build - ./waf distcheck +distclean: + $(WAFCMD) distclean + -rm -rf doc/_build/ + -rm -rf doc/web/ + +check_distclean: + make distclean + +distcheck: checkwaf + $(WAFCMD) distcheck $(WAFOPTS) help: - ./waf --help + $(WAFCMD) --help + +create_test_sounds: + -[ -z `which $(SOX)` ] && ( echo $(SOX) could not be found) || true + -mkdir -p $(TESTSOUNDS) + -$(SOX) -r 44100 -b 16 -n "$(TESTSOUNDS)/44100Hz_1f_silence.wav" trim 0 1s + -$(SOX) -r 22050 -b 16 -n "$(TESTSOUNDS)/22050Hz_5s_brownnoise.wav" synth 5 brownnoise vol 0.9 + -$(SOX) -r 32000 -b 16 -n "$(TESTSOUNDS)/32000Hz_127f_sine440.wav" synth 127s sine 440 vol 0.9 + -$(SOX) -r 8000 -b 16 -n "$(TESTSOUNDS)/8000Hz_30s_silence.wav" trim 0 30 + -$(SOX) -r 48000 -b 32 -n "$(TESTSOUNDS)/48000Hz_60s_sweep.wav" synth 60 sine 100-20000 vol 0.9 + -$(SOX) -r 44100 -b 16 -n "$(TESTSOUNDS)/44100Hz_44100f_sine441.wav" synth 44100s sine 441 vol 0.9 + -$(SOX) -r 44100 -b 16 -n "$(TESTSOUNDS)/44100Hz_100f_sine441.wav" synth 100s sine 441 vol 0.9 + +# build only libaubio, no python-aubio +test_lib_only: clean distclean configure build install list_installed +# additionally, clean after a fresh build +test_lib_only_clean: test_lib_only uninstall check_clean check_distclean + +# build libaubio, build and test python-aubio against it +test_lib_python: force_uninstall_python deps_python \ + clean_python clean distclean \ + configure build build_python \ + install install_python \ + test_python \ + list_all_installed + +test_lib_python_clean: test_lib_python \ + uninstall_python uninstall \ + check_clean_python \ + check_clean \ + check_distclean + +# build libaubio, install it, build python-aubio against it +test_lib_install_python: force_uninstall_python deps_python \ + clean_python distclean \ + configure build \ + install \ + build_python_extlib \ + install_python \ + test_python \ + list_all_installed + +test_lib_install_python_clean: test_lib_install_python \ + uninstall_python \ + delete_install \ + check_clean_python \ + check_distclean + +# build a python-aubio that includes libaubio +test_python_only: force_uninstall_python deps_python \ + clean_python clean distclean \ + build_python \ + install_python \ + test_python \ + list_installed_python + +test_python_only_clean: test_python_only \ + uninstall_python \ + check_clean_python + +sphinx: configure + $(WAFCMD) sphinx $(WAFOPTS) + +doxygen: configure + $(WAFCMD) doxygen $(WAFOPTS) + +manpages: configure + $(WAFCMD) manpages $(WAFOPTS) + +html: doxygen sphinx + +docs: html manpages + +dist: distclean expandwaf + $(WAFCMD) dist diff --git a/README.md b/README.md index 8957a362..adaf1282 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ Python module ------------- A python module to access the library functions is also provided. Please see -the file `python/README` for more information on how to use it. +the file [`python/README.md`](python/README.md) for more information on how to +use it. Examples tools -------------- @@ -70,8 +71,8 @@ The latest version of the documentation can be found at: https://aubio.org/documentation -Installation and Build Instructions ------------------------------------ +Build Instructions +------------------ A number of distributions already include aubio. Check your favorite package management system, or have a look at the [download @@ -81,13 +82,41 @@ aubio uses [waf](https://waf.io/) to configure, compile, and test the source: ./waf configure ./waf build - sudo ./waf install If waf is not found in the directory, you can download and install it with: make getwaf -aubio compiles on Linux, Mac OS X, Cygwin, and iOS. +aubio compiles on Linux, Mac OS X, Windows, Cygwin, and iOS. + +Installation +------------ + +To install aubio library and headers on your system, use: + + sudo ./waf install + +To uninstall: + + sudo ./waf uninstall + +If you don't have root access to install libaubio on your system, you can use +libaubio without installing libaubio either by setting `LD_LIBRARY_PATH`, or by +copying it to `~/lib`. + +On Linux, you should be able to set `LD_LIBRARY_PATH` with: + + $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/build/src + +On Mac OS X, a copy or a symlink can be made in `~/lib`: + + $ mkdir -p ~/lib + $ ln -sf $PWD/build/src/libaubio*.dylib ~/lib/ + +Note on Mac OS X systems older than El Capitan (10.11), the `DYLD_LIBRARY_PATH` +variable can be set as follows: + + $ export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$PWD/build/src Credits and Publications ------------------------ @@ -133,7 +162,7 @@ Questions, comments, suggestions, and contributions are welcome. Use the mailing list: . To subscribe to the list, use the mailman form: -http://lists.aubio.org/listinfo/aubio-user/ +https://lists.aubio.org/listinfo/aubio-user/ Alternatively, feel free to contact directly the author. @@ -141,7 +170,7 @@ Alternatively, feel free to contact directly the author. Copyright and License Information --------------------------------- -Copyright (C) 2003-2013 Paul Brossier +Copyright (C) 2003-2016 Paul Brossier aubio is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software diff --git a/VERSION b/VERSION index f1a8a484..2a2b20aa 100644 --- a/VERSION +++ b/VERSION @@ -1,7 +1,7 @@ AUBIO_MAJOR_VERSION=0 AUBIO_MINOR_VERSION=4 -AUBIO_PATCH_VERSION=3 +AUBIO_PATCH_VERSION=5 AUBIO_VERSION_STATUS='~alpha' -LIBAUBIO_LT_CUR=4 -LIBAUBIO_LT_REV=2 -LIBAUBIO_LT_AGE=2 +LIBAUBIO_LT_CUR=5 +LIBAUBIO_LT_REV=1 +LIBAUBIO_LT_AGE=5 diff --git a/doc/Makefile b/doc/Makefile index 8b0b81d1..b93571d0 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -39,9 +39,11 @@ help: @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: + -rm -rf _static -rm -rf $(BUILDDIR)/* html: + mkdir -p _static $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." diff --git a/doc/android.rst b/doc/android.rst new file mode 100644 index 00000000..c46eb309 --- /dev/null +++ b/doc/android.rst @@ -0,0 +1,9 @@ +.. _android: + +Building aubio for Android +-------------------------- + +To compile aubio for android, you will need to get the `Android Native +Development Toolkit (NDK) `_, prepare a +standalone toolchain, and tell waf to use the NDK toolchain. An example script +to complete these tasks is available in ``scripts/build_android``. diff --git a/doc/aubiocut.txt b/doc/aubiocut.txt index be19dad7..8023460d 100644 --- a/doc/aubiocut.txt +++ b/doc/aubiocut.txt @@ -34,9 +34,9 @@ OPTIONS -b, --beat Use beat locations instead of onset locations. -t, --onset-threshold thres Set the threshold value for the onset peak - picking. Typical values are typically within 0.001 and 0.900. Defaults to - 0.1. Lower threshold values imply more onsets detected. Try 0.5 in case of - over-detections. Defaults to 0.3. + picking. Values are typically in the range [0.001, 0.900]. Lower threshold + values imply more onsets detected. Increasing this threshold should reduce + the number of incorrect detections. Defaults to 0.3. -c, --cut Cut input sound file at detected labels. A new sound files for each slice will be created in the current directory. diff --git a/doc/aubiomfcc.txt b/doc/aubiomfcc.txt index afeafe38..6398d9ae 100644 --- a/doc/aubiomfcc.txt +++ b/doc/aubiomfcc.txt @@ -6,6 +6,7 @@ SYNOPSIS aubiomfcc source aubiomfcc [[-i] source] [-r rate] [-B win] [-H hop] + [-T time-format] [-v] [-h] DESCRIPTION @@ -37,6 +38,9 @@ OPTIONS -H, --hopsize hop The number of samples between two consecutive analysis. Defaults to 256. + -T, --timeformat format Set time format (samples, ms, seconds). Defaults to + seconds. + -h, --help Print a short help message and exit. -v, --verbose Be verbose. diff --git a/doc/aubionotes.txt b/doc/aubionotes.txt index 190fc72e..0355040c 100644 --- a/doc/aubionotes.txt +++ b/doc/aubionotes.txt @@ -8,6 +8,7 @@ SYNOPSIS [-r rate] [-B win] [-H hop] [-O method] [-t thres] [-p method] [-u unit] [-l thres] + [-T time-format] [-s sil] [-j] [-v] [-h] @@ -49,6 +50,9 @@ OPTIONS 0.1. Lower threshold values imply more onsets detected. Try 0.5 in case of over-detections. Defaults to 0.3. + -M, --minioi value Set the minimum inter-onset interval, in seconds, the + shortest interval between two consecutive notes. Defaults to 0.030 + -p, --pitch method The pitch detection method to use. See PITCH METHODS below. Defaults to 'default'. @@ -64,6 +68,9 @@ OPTIONS 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. + -T, --timeformat format Set time format (samples, ms, seconds). Defaults to + seconds. + -j, --jack Use Jack input/output. You will need a Jack connection controller to feed aubio some signal and listen to its output. diff --git a/doc/aubioonset.txt b/doc/aubioonset.txt index f9d3783d..e3cb5601 100644 --- a/doc/aubioonset.txt +++ b/doc/aubioonset.txt @@ -7,8 +7,10 @@ SYNOPSIS aubioonset [[-i] source] [-o sink] [-r rate] [-B win] [-H hop] [-O method] [-t thres] + [-T time-format] [-s sil] [-m] [-f] - [-j] [-v] [-h] + [-j] [-N miditap-note] [-V miditap-velo] + [-v] [-h] DESCRIPTION @@ -47,14 +49,20 @@ OPTIONS below. Defaults to 'default'. -t, --onset-threshold thres Set the threshold value for the onset peak - picking. Typical values are typically within 0.001 and 0.900. Defaults to - 0.1. Lower threshold values imply more onsets detected. Try 0.5 in case of - over-detections. Defaults to 0.3. + picking. Values are typically in the range [0.001, 0.900]. Lower threshold + values imply more onsets detected. Increasing this threshold should reduce + the number of incorrect detections. Defaults to 0.3. - -s, --silence sil Set the silence threshold, in dB, under which the pitch + -M, --minioi value Set the minimum inter-onset interval, in seconds, the + shortest interval between two consecutive onsets. Defaults to 0.020 + + -s, --silence sil Set the silence threshold, in dB, under which the onset 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. + -T, --timeformat format Set time format (samples, ms, seconds). Defaults to + seconds. + -m, --mix-input Mix source signal to the output signal before writing to sink. @@ -63,6 +71,10 @@ OPTIONS -j, --jack Use Jack input/output. You will need a Jack connection controller to feed aubio some signal and listen to its output. + -N, --miditap-note Override note value for MIDI tap. Defaults to 69. + + -V, --miditap-velop Override velocity value for MIDI tap. Defaults to 65. + -h, --help Print a short help message and exit. -v, --verbose Be verbose. diff --git a/doc/aubiopitch.txt b/doc/aubiopitch.txt index 1fc8205d..1313975c 100644 --- a/doc/aubiopitch.txt +++ b/doc/aubiopitch.txt @@ -7,6 +7,7 @@ SYNOPSIS aubiopitch [[-i] source] [-o sink] [-r rate] [-B win] [-H hop] [-p method] [-u unit] [-l thres] + [-T time-format] [-s sil] [-f] [-v] [-h] [-j] @@ -59,6 +60,9 @@ OPTIONS 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. + -T, --timeformat format Set time format (samples, ms, seconds). Defaults to + seconds. + -m, --mix-input Mix source signal to the output signal before writing to sink. diff --git a/doc/aubioquiet.txt b/doc/aubioquiet.txt index eb11ae04..94283158 100644 --- a/doc/aubioquiet.txt +++ b/doc/aubioquiet.txt @@ -6,6 +6,7 @@ SYNOPSIS aubioquiet source aubioquiet [[-i] source] [-r rate] [-B win] [-H hop] + [-T time-format] [-s sil] [-v] [-h] @@ -38,6 +39,9 @@ OPTIONS -s, --silence sil Set the silence threshold, in dB, under which the pitch will not be detected. Defaults to -90.0. + -T, --timeformat format Set time format (samples, ms, seconds). Defaults to + seconds. + -h, --help Print a short help message and exit. -v, --verbose Be verbose. diff --git a/doc/aubiotrack.txt b/doc/aubiotrack.txt index 753e97f8..263157fb 100644 --- a/doc/aubiotrack.txt +++ b/doc/aubiotrack.txt @@ -6,8 +6,10 @@ SYNOPSIS aubiotrack source aubiotrack [[-i] source] [-o sink] [-r rate] [-B win] [-H hop] + [-T time-format] [-s sil] [-m] - [-j] [-v] [-h] + [-j] [-N miditap-note] [-V miditap-velo] + [-v] [-h] DESCRIPTION @@ -53,6 +55,13 @@ OPTIONS -j, --jack Use Jack input/output. You will need a Jack connection controller to feed aubio some signal and listen to its output. + -N, --miditap-note Override note value for MIDI tap. Defaults to 69. + + -V, --miditap-velop Override velocity value for MIDI tap. Defaults to 65. + + -T, --timeformat format Set time format (samples, ms, seconds). Defaults to + seconds. + -h, --help Print a short help message and exit. -v, --verbose Be verbose. diff --git a/doc/building.rst b/doc/building.rst new file mode 100644 index 00000000..5c52a309 --- /dev/null +++ b/doc/building.rst @@ -0,0 +1,98 @@ +.. highlight:: bash + +.. _building: + +Building aubio +============== + +.. note:: + To download a prebuilt version of aubio, see :ref:`download`. + +aubio uses `waf`_ to configure, compile, and test the source. +A copy of waf is included in aubio tarball, so all you need is a terminal, +a compiler, and a recent version of python installed. + +.. note:: + Make sure you have all the :ref:`requirements` you want before building. + +Latest release +-------------- + +The **latest stable release** can be downloaded from https://aubio.org/download:: + + $ curl -O http://aubio.org/pub/aubio-0.4.3.tar.bz2 + $ tar xf aubio-0.4.3.tar.bz2 + $ cd aubio-0.4.3 + +Git repository +-------------- + +The **latest git branch** can be obtained with:: + + $ git clone git://git.aubio.org/git/aubio + $ cd aubio + +The following command will fetch the correct `waf`_ version (not included in +aubio's git):: + + $ ./scripts/get_waf.sh + +.. note:: + + Windows users without `Git Bash`_ installed will want to use the following + commands instead: + + .. code:: bash + + $ curl -fsS -o waf https://waf.io/waf-1.8.22 + $ curl -fsS -o waf.bat https://raw.githubusercontent.com/waf-project/waf/master/utils/waf.bat + + +Compiling +--------- + +To compile the C library, examples programs, and tests, run:: + + $ ./waf configure + +Check out the available options using ``./waf configure --help``. Once +you are done with configuration, you can start building:: + + $ ./waf build + +To install the freshly built C library and tools, simply run the following +command:: + + $ sudo ./waf install + +.. note:: + Windows users should simply run ``waf``, without the leading ``./``. For + instance: + + .. code:: bash + + $ waf configure build + +Cleaning +-------- + +If you wish to uninstall the files installed by the ``install`` command, use +``uninstall``:: + + $ sudo ./waf uninstall + +To clean the source directory, use the ``clean`` command:: + + $ ./waf clean + +To also forget the options previously passed to the last ``./waf configure`` +invocation, use the ``distclean`` command:: + + $ ./waf distclean + +.. _waf: https://waf.io/ + +.. _Git Bash: https://git-for-windows.github.io/ + +.. toctree:: + :maxdepth: 2 diff --git a/doc/cli.rst b/doc/cli.rst new file mode 100644 index 00000000..2dc2f070 --- /dev/null +++ b/doc/cli.rst @@ -0,0 +1,59 @@ +.. _manpages: + +Command line tools +================== + +A few simple command line tools are included along with the library. + + - ``aubioonset`` outputs the time stamp of detected note onsets + - ``aubiopitch`` attempts to identify a fundamental frequency, or pitch, for + each frame of the input sound + - ``aubiomfcc`` computes Mel-frequency Cepstrum Coefficients + - ``aubiotrack`` outputs the time stamp of detected beats + - ``aubionotes`` emits midi-like notes, with an onset, a pitch, and a duration + - ``aubioquiet`` extracts quiet and loud regions + +Additionally, the python module comes with the following script: + + - ``aubiocut`` slices sound files at onset or beat timestamps + + +.. toctree:: + + cli_features + + +``aubioonset`` +-------------- + +.. literalinclude:: aubioonset.txt + +``aubiopitch`` +-------------- + +.. literalinclude:: aubiopitch.txt + +``aubiomfcc`` +-------------- + +.. literalinclude:: aubiomfcc.txt + +``aubiotrack`` +-------------- + +.. literalinclude:: aubiotrack.txt + +``aubionotes`` +-------------- + +.. literalinclude:: aubionotes.txt + +``aubioquiet`` +-------------- + +.. literalinclude:: aubioquiet.txt + +``aubiocut`` +-------------- + +.. literalinclude:: aubiocut.txt diff --git a/doc/cli_features.rst b/doc/cli_features.rst new file mode 100644 index 00000000..ab77c3a1 --- /dev/null +++ b/doc/cli_features.rst @@ -0,0 +1,42 @@ +Command line features +===================== + ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| feat vs. prg | onset | pitch | mfcc | track | notes | quiet | cut1 | short options | ++==============+=======+=======+======+=======+=======+=======+======+==================+ +| input | Y | Y | Y | Y | Y | Y | Y | -i | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| output | Y | Y | N | Y | Y | N | Y!1 | -o,-m,-f | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| Hz/buf/hop | Y | Y | Y | Y | Y | Y!2 | Y | -r,-B-,H | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| jack | Y | Y | N | Y | Y | N!3 | N | -j | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| onset | Y | N | N | Y!8 | Y!6 | N | Y | -O,-t,-M | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| pitch | N | Y | N | N | Y!6 | N | N!5 | -p,-u,-l | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| silence | Y | Y | N | Y | Y!7 | Y | N!4 | -s | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| timefmt | Y | Y | Y | Y | Y | Y | ! | -T | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| help | Y | Y | Y | Y | Y | Y | Y | -h | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ +| verbose | Y | Y | Y | Y | Y | Y | Y | -v | ++--------------+-------+-------+------+-------+-------+-------+------+------------------+ + +1. ``aubiocut --output`` is used to specify a directory, not a file. + +2. Option ``--bufsize`` is useless for ``aubioquiet`` + +3. ``aubioquiet`` could have a jack output + +4. Regression, re-add slicing at silences to ``aubiocut`` + +5. ``aubiocut`` could cut on notes + +6. ``aubionotes`` needs onset/pitch setters. + +7. Silence was different for pitch and onset, test. + +8. Some ``aubiotrack`` options should be disabled (minioi, threshold). diff --git a/doc/conf.py b/doc/conf.py index 48e5a4e3..ad030c6a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -16,7 +16,7 @@ import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../../python/build/lib.macosx-10.6-intel-2.7')) +#sys.path.insert(0, os.path.abspath('../../python/build/...')) # -- General configuration ----------------------------------------------------- @@ -41,7 +41,7 @@ master_doc = 'index' # General information about the project. project = u'aubio' -copyright = u'2014, Paul Brossier' +copyright = u'2016, 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 @@ -50,7 +50,7 @@ copyright = u'2014, Paul Brossier' # The short X.Y version. version = '0.4' # The full version, including alpha/beta/rc tags. -release = 'latest' +release = '0.4.5~alpha' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -91,7 +91,10 @@ modindex_common_prefix = ['aubio.'] # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +#html_theme = 'agogo' +#html_theme = 'default' +#html_theme = 'haiku' +html_theme = 'pyramid' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -120,7 +123,7 @@ html_theme = 'default' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = [] #['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. @@ -150,7 +153,7 @@ html_static_path = ['_static'] #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True @@ -240,3 +243,6 @@ texinfo_documents = [ # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' + +def setup(app): + if release.endswith('~alpha'): app.tags.add('devel') diff --git a/doc/debian_packages.rst b/doc/debian_packages.rst new file mode 100644 index 00000000..6680f1aa --- /dev/null +++ b/doc/debian_packages.rst @@ -0,0 +1,16 @@ +Debian/Ubuntu packages +---------------------- + +For the latest Debian packages, see https://packages.debian.org/src:aubio. + +For the latest Ubuntu packages, see http://packages.ubuntu.com/src:aubio. + +For the latest version of the packages, see +https://anonscm.debian.org/cgit/collab-maint/aubio.git/. Use +``git-buildpackage`` to build from the git repository. For instance: + +.. code-block:: bash + + $ git clone git://anonscm.debian.org/collab-maint/aubio.git + $ cd aubio + $ git buildpackage diff --git a/doc/develop.rst b/doc/develop.rst new file mode 100644 index 00000000..cb39dd78 --- /dev/null +++ b/doc/develop.rst @@ -0,0 +1,102 @@ +.. _develop: + +Developping with aubio +====================== + +Read `Contribute`_ to report issues and request new features. + +See `Doxygen documentation`_ for the complete documentation of the C library, +built using `Doxygen `_. + +Below is a brief `Library overview`_. + +Library overview +---------------- + +Here is a brief overview of the C library. See also the `Doxygen +documentation`_ for a more detailed list of available functions. + +Vectors and matrix +`````````````````` + +``fvec_t`` are used to hold vectors of float (``smpl_t``). + +.. literalinclude:: ../tests/src/test-fvec.c + :language: C + :lines: 7 + + +.. code-block:: C + + // set some elements + vec->data[511] = 2.; + vec->data[vec->length-2] = 1.; + +Similarly, ``fmat_t`` are used to hold matrix of floats. + +.. literalinclude:: ../tests/src/test-fmat.c + :language: C + :lines: 9-19 + +Reading a sound file +```````````````````` +In this example, ``aubio_source`` is used to read a media file. + +First, create the objects we need. + +.. literalinclude:: ../tests/src/io/test-source.c + :language: C + :lines: 22-24, 30-32, 34 + +.. note:: + With ``samplerate = 0``, ``aubio_source`` will be created with the file's + original samplerate. + +Now for the processing loop: + +.. literalinclude:: ../tests/src/io/test-source.c + :language: C + :lines: 40-44 + +At the end of the processing loop, clean-up and de-allocate memory: + +.. literalinclude:: ../tests/src/io/test-source.c + :language: C + :lines: 50-56 + +See the complete example: :download:`test-source.c +<../tests/src/io/test-source.c>`. + +Computing the spectrum +`````````````````````` + +Now let's create a phase vocoder: + +.. literalinclude:: ../tests/src/spectral/test-phasevoc.c + :language: C + :lines: 6-11 + +The processing loop could now look like: + +.. literalinclude:: ../tests/src/spectral/test-phasevoc.c + :language: C + :lines: 21-35 + +See the complete example: :download:`test-phasevoc.c +<../tests/src/spectral/test-phasevoc.c>`. + +.. _doxygen-documentation: + +Doxygen documentation +--------------------- + +The latest version of the doxygen documentation is available at: + + https://aubio.org/doc/latest + +Contribute +---------- + +Please report any issue and feature request at the `Github issue tracker +`_. Patches and pull-requests welcome! + diff --git a/doc/download.rst b/doc/download.rst new file mode 100644 index 00000000..a80cf2d8 --- /dev/null +++ b/doc/download.rst @@ -0,0 +1,20 @@ +.. _download: + +Downloading aubio +================= + +A number of distributions already include aubio. Check your favorite package +management system, or have a look at the `aubio download page +`_ for more options. + +To use aubio in a macOS or iOS application, see :ref:`xcode-frameworks-label`. + +To use aubio in an android project, see :ref:`android`. + +.. toctree:: + + debian_packages + xcode_frameworks + android + +To compile aubio from source, read :ref:`building`. diff --git a/doc/full.cfg b/doc/full.cfg index 9fc2225a..f7d8d848 100644 --- a/doc/full.cfg +++ b/doc/full.cfg @@ -1,4 +1,4 @@ -# Doxyfile 1.8.5 +# Doxyfile 1.8.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -38,7 +38,7 @@ PROJECT_NAME = aubio # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "0.4.2~alpha full" +PROJECT_NUMBER = "0.4.5~alpha" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -70,15 +70,25 @@ OUTPUT_DIRECTORY = full CREATE_SUBDIRS = NO +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- -# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, -# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, -# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, -# Turkish, Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English @@ -259,9 +269,12 @@ OPTIMIZE_OUTPUT_VHDL = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # @@ -500,6 +513,13 @@ HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -521,7 +541,8 @@ SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO @@ -659,8 +680,7 @@ LAYOUT_FILE = # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. +# search path. See also \cite for info how to create references. CITE_BIB_FILES = @@ -959,6 +979,25 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1051,13 +1090,15 @@ HTML_FOOTER = HTML_STYLESHEET = -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra stylesheet files is of importance (e.g. the last +# stylesheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1222,7 +1263,8 @@ GENERATE_CHI = NO CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1422,7 +1464,7 @@ MATHJAX_FORMAT = HTML-CSS # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example @@ -1462,11 +1504,11 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. There -# are two flavours of web server based searching depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. See -# the section "External Indexing and Searching" for details. +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1594,17 +1636,19 @@ EXTRA_PACKAGES = # # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will -# replace them by respectively the title of the page, the current date and time, -# only the current date, the version number of doxygen, the project name (see -# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, +# for the replacement values of the other commands the user is refered to +# HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. # # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1628,7 +1672,7 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate # the PDF file directly from the LaTeX files. Set this option to YES to get a # higher quality PDF documentation. # The default value is: YES. @@ -1754,6 +1798,13 @@ MAN_OUTPUT = man MAN_EXTENSION = .3 +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without @@ -1781,18 +1832,6 @@ GENERATE_XML = NO XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify a XML DTD, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_DTD = - # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size @@ -1820,6 +1859,15 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook +# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -1939,9 +1987,9 @@ PREDEFINED = EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an -# all uppercase name, and do not end with a semicolon. Such function macros are -# typically used for boiler-plate code, and will confuse the parser if not +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1961,7 +2009,7 @@ SKIP_FUNCTION_MACROS = YES # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. -# Note: Each tag file must have an unique name (where the name does NOT include +# Note: Each tag file must have a unique name (where the name does NOT include # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. @@ -2021,6 +2069,13 @@ CLASS_DIAGRAMS = YES MSCGEN_PATH = +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + # If set to YES, the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2032,7 +2087,7 @@ HIDE_UNDOC_RELATIONS = NO # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. HAVE_DOT = NO @@ -2046,7 +2101,7 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font n the dot files that doxygen +# When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make # sure dot is able to find the font, which can be done by putting it in a # standard location or by setting the DOTFONTPATH environment variable or by @@ -2184,7 +2239,9 @@ DIRECTORY_GRAPH = YES # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd and svg. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2221,6 +2278,21 @@ DOTFILE_DIRS = MSCFILE_DIRS = +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. +# This tag requires that the tag HAVE_DOT is set to YES. + +PLANTUML_JAR_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized diff --git a/doc/index.rst b/doc/index.rst index 70b9df8c..c9dfc849 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,16 +1,51 @@ -aubio documentation -=================== +Welcome +======= -aubio is a collection of algorithms and tools to label music and sounds. It -listens to audio signals and attempts to detect events. For instance, when a -drum is hit, at which frequency is a note, or at what tempo is a rhythmic -melody. +aubio is a collection of algorithms and tools to label and transform music and +sounds. It scans or `listens` to audio signals and attempts to detect musical +events. For instance, when a drum is hit, at which frequency is a note, or at +what tempo is a rhythmic melody. -Its features include segmenting a sound file before each of its attacks, +aubio features include segmenting a sound file before each of its attacks, performing pitch detection, tapping the beat and producing midi streams from live audio. -aubio provide several algorithms and routines, including: +Quick links +=========== + +* :ref:`python` +* :ref:`manpages` +* :ref:`develop` +* :ref:`building` + +.. only:: devel + + .. include:: statuslinks.rst + +Project pages +============= + +* `Project homepage`_: https://aubio.org +* `aubio on github`_: https://github.com/aubio/aubio +* `aubio on pypi`_: https://pypi.python.org/pypi/aubio +* `Doxygen documentation`_: https://aubio.org/doc/latest/ +* `Mailing lists`_: https://lists.aubio.org + +.. _Project homepage: https://aubio.org +.. _aubio on github: https://github.com/aubio/aubio +.. _aubio on pypi: https://pypi.python.org/pypi/aubio +.. _Doxygen documentation: https://aubio.org/doc/latest/ +.. _Mailing lists: https://lists.aubio.org/ + +* `Travis Continuous integration page `_ +* `Appveyor Continuous integration page `_ +* `Landscape python code validation `_ +* `ReadTheDocs documentation `_ + +Features +======== + +aubio provides several algorithms and routines, including: - several onset detection methods - different pitch detection methods @@ -21,93 +56,40 @@ aubio provide several algorithms and routines, including: - digital filters (low pass, high pass, and more) - spectral filtering - transient/steady-state separation -- sound file and audio devices read and write access +- sound file read and write access - various mathematics utilities for music applications The name aubio comes from *audio* with a typo: some errors are likely to be found in the results. -Python module -------------- - -A python module to access the library functions is also provided. Please see -the file ``python/README`` for more information on how to use it. - -Examples tools --------------- - -A few simple command line tools are included along with the library: - - - ``aubioonset`` outputs the time stamp of detected note onsets - - ``aubiopitch`` attempts to identify a fundamental frequency, or pitch, for - each frame of the input sound - - ``aubiomfcc`` computes Mel-frequency Cepstrum Coefficients - - ``aubiotrack`` outputs the time stamp of detected beats - - ``aubionotes`` emits midi-like notes, with an onset, a pitch, and a duration - - ``aubioquiet`` extracts quiet and loud regions - -Additionally, the python module comes with the following script: - - - ``aubiocut`` slices sound files at onset or beat timestamps - -C API basics ------------- - -The library is written in C and is optimised for speed and portability. - -The C API is designed in the following way: - -.. code-block:: c - - aubio_something_t * new_aubio_something(void * args); - audio_something_do(aubio_something_t * t, void * args); - smpl_t aubio_something_get_a_parameter(aubio_something_t * t); - uint_t aubio_something_set_a_parameter(aubio_something_t * t, smpl_t a_parameter); - void del_aubio_something(aubio_something_t * t); - -For performance and real-time operation, no memory allocation or freeing take -place in the ``_do`` methods. Instead, memory allocation should always take place -in the ``new_`` methods, whereas free operations are done in the ``del_`` methods. - -.. code-block:: bash - - ./waf configure - ./waf build - sudo ./waf install - -aubio compiles on Linux, Mac OS X, Cygwin, and iPhone. - -Documentation -------------- - -- Manual pages: http://aubio.org/documentation -- API documentation: http://aubio.org/doc/latest/ - -Contribute ----------- - -- Issue Tracker: https://github.com/piem/aubio/issues -- Source Code: https://github.com/piem/aubio - -Contact info ------------- +Copyright +========= -The home page of this project can be found at: http://aubio.org/ +Copyright © 2003-2016 Paul Brossier -Questions, comments, suggestions, and contributions are welcome. Use the -mailing list: . +License +======= -To subscribe to the list, use the mailman form: -http://lists.aubio.org/listinfo/aubio-user/ +aubio is a `free `_ and `open source +`_ software; **you** can +redistribute it and/or modify it under the terms of the `GNU +`_ `General Public License +`_ as published by the `Free Software +Foundation `_, either version 3 of the License, or (at your +option) any later version. -Alternatively, feel free to contact directly the author. +.. note:: + aubio is not MIT or BSD licensed. Contact the author if you need it in your + commercial product. -Contents --------- +Content +======= .. toctree:: - :maxdepth: 1 + :maxdepth: 2 installing python_module + cli + develop diff --git a/doc/installing.rst b/doc/installing.rst index 6629e385..defbcc32 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -1,65 +1,19 @@ -.. highlight:: bash - Installing aubio ================ -A number of distributions already include aubio. Check your favorite package -management system, or have a look at the `download page -`_. - -aubio uses `waf `_ to configure, compile, and test the source. -A copy of ``waf`` is included along aubio, so all you need is a ``terminal`` -and a recent ``python`` installed. - -Source code ------------ - -Check out the `download page `_ for more options: -http://aubio.org/download. - -The latest stable release can be found at http://aubio.org/pub/:: - - $ curl -O http://aubio.org/pub/aubio-0.4.1.tar.bz2 - $ tar xf aubio-0.4.1.tar.bz2 - $ cd aubio-0.4.1 - -The latest develop branch can be obtained with:: - - $ git clone git://git.aubio.org/git/aubio/ aubio-devel - $ cd aubio-devel - $ git fetch origin develop:develop - $ git checkout develop - -Compiling ---------- - -To compile the C library, examples programs, and tests, run:: - - $ ./waf configure - -Check out the available options using ``./waf configure --help | less``. Once -you are done with configuration, you can start building:: - - $ ./waf build - -To install the freshly built C library and tools, simply run the following -command:: - - $ sudo ./waf install - -Cleaning --------- - -If you wish to uninstall the files installed by the ``install`` command, use -``uninstall``:: +aubio runs on Linux, Windows, macOS, iOS, Android, and probably a few others +operating systems. - $ sudo ./waf uninstall +To download a pre-compiled version of the library, head to :ref:`download`. -To clean the source directory, use the ``clean`` command:: +To install the python extension, head to :ref:`python`. - $ ./waf clean +To compile aubio form source, first check the :ref:`requirements`, then read +:ref:`building`. -To also forget the options previously passed to the last ``./waf configure`` -invocation, use the ``distclean`` command:: +.. toctree:: + :maxdepth: 2 - $ ./waf distclean + download + building + requirements diff --git a/doc/python_module.rst b/doc/python_module.rst index eab5cd4a..ed834f56 100644 --- a/doc/python_module.rst +++ b/doc/python_module.rst @@ -1,5 +1,18 @@ -aubio Python module -=================== +.. _python: + +Python module +============= + +The aubio extension for Python is available for Python 2.7 and Python 3. + +Installing aubio with pip +------------------------- + +aubio can now be installed using ``pip``: + +.. code-block:: bash + + $ pip install aubio Building the module ------------------- @@ -8,26 +21,53 @@ From ``aubio`` source directory, run the following: .. code-block:: bash - $ cd python + $ ./setup.py clean $ ./setup.py build $ sudo ./setup.py install -Using the module ----------------- +Using aubio in python +--------------------- + +Once you have python-aubio installed, you should be able to run ``python -c +"import aubio; print(aubio.version)"``. + +A simple example +................ + +Here is a :download:`simple script <../python/demos/demo_source_simple.py>` +that reads all the samples from a media file: + +.. literalinclude:: ../python/demos/demo_source_simple.py + :language: python + +Filtering an input sound file +............................. + +Here is a more complete example, :download:`demo_filter.py +<../python/demos/demo_filter.py>`. This files executes the following: + +* read an input media file (``aubio.source``) + +* filter it using an `A-weighting `_ + filter (``aubio.digital_filter``) + +* write result to a new file (``aubio.sink``) + +.. literalinclude:: ../python/demos/demo_filter.py + :language: python -To use the python module, simply import aubio: +More demos +.......... -.. code-block:: python +Check out the `python demos folder`_ for more examples. - #! /usr/bin/env python - import aubio +Python tests +------------ - s = aubio.source(sys.argv[1], 0, 256) - while True: - samples, read = s() - print samples - if read < 256: break +A number of `python tests`_ are provided. To run them, use +``python/tests/run_all_tests``. -Check out the `python demos for aubio -`_ for more examples. +.. _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 diff --git a/doc/requirements.rst b/doc/requirements.rst new file mode 100644 index 00000000..8a74eb0a --- /dev/null +++ b/doc/requirements.rst @@ -0,0 +1,242 @@ +.. _requirements: + +Build options +============= + +If built without any external dependencies aubio can be somewhat useful, for +instance to read, process, and write simple wav files. + +To support more media input formas add more features to aubio, you can use one +or all of the `following libraries `_. + +You may also want to know more about the `other options`_ and the `platform +notes`_ + +The configure script will automatically for these extra libraries. To make sure +the library or feature is used, pass the `--enable-flag` to waf. To disable +this feature, use `--disable-feature`. + +To find out more about the build commands, use the `--verbose` option. + +External libraries +------------------ + +External libraries are checked for using ``pkg-config``. Set the +``PKG_CONFIG_PATH`` environment variable if you have them installed in an +unusual location. + + +.. note:: + + If ``pkg-config`` is not found in ``PATH``, the configure step will + succeed, but none of the external libraries will be used. + +libav +..... + + `libav.org `_, open source audio and video processing + tools. + +If all of the following libraries are found, they will be used to compile +``aubio_source_avcodec``. so that ``aubio_source`` will be able to decode audio +from all formats supported by `libav +`_. + +* libavcodec +* libavformat +* libavutil +* libavresample + +To enable this option, configure with ``--enable-avcodec``. The build will then +failed if the required libraries are not found. To disable this option, +configure with ``--disable-avcodec`` + + +libsndfile +.......... + + `libsndfile `_, a C library for reading + and writing sampled sound files. + +With libsndfile built in, ``aubio_source_sndfile`` will be built in and used by +``aubio_source``. + +To enable this option, configure with ``--enable-sndfile``. The build will then +fail if the required library is not found. To disable this option, configure +with ``--disable-sndfile`` + +libsamplerate +............. + + `libsamplerate `_, a sample rate converter for + audio. + +With libsamplerate built in, ``aubio_source_sndfile`` will support resampling, +and ``aubio_resample`` will be fully functional. + +To enable this option, configure with ``--enable-samplerate``. The build will +then fail if the required library is not found. To disable this option, +configure with ``--disable-samplerate`` + +libfftw3 +........ + + `FFTW `_, a C subroutine for computing the discrete Fourier + transform + +With libfftw3 built in, ``aubio_fft`` will use `FFTW`_ to +compute Fast Fourier Transform (FFT), allowing aubio to compute FFT on length +that are not a power of 2. + +To enable this option, configure with ``--enable-fftw3``. The build will +then fail if the required library is not found. To disable this option, +configure with ``--disable-fftw3`` + +Platform notes +-------------- + +On all platforms, you will need to have installed: + + - a compiler (gcc, clang, msvc, ...) + - python (any version >= 2.7, including 3.x) + - a terminal to run command lines in + +Linux +..... + +The following `External libraries`_ will be used if found: `libav`_, +`libsamplerate`_, `libsndfile`_, `libfftw3`_. + +macOS +..... + +The following system frameworks will be used on Mac OS X systems: + + - `Accelerate `_ to compute + FFTs and other vectorized operations optimally. + + - `CoreAudio `_ and + `AudioToolbox `_ to + decode audio from files and network streams. + +.. note:: + + To build a fat binary for both ``i386`` and ``x86_64``, use ``./waf configure + --enable-fat``. + +The following `External libraries`_ will also be checked: `libav`_, +`libsamplerate`_, `libsndfile`_, `libfftw3`_. + +To build a fat binary on a darwin like system (macOS, tvOS, appleOS, ...) +platforms, configure with ``--enable-fat``. + +Windows +....... + +To use a specific version of the compiler, ``--msvc_version``. To build for a +specific architecture, use ``--msvc_target``. For instance, to build aubio +for ``x86`` using ``msvc 12.0``, use: + +.. code:: bash + + waf configure --msvc_version='msvc 12.0' --msvc_target='x86' + + +The following `External libraries`_ will be used if found: `libav`_, +`libsamplerate`_, `libsndfile`_, `libfftw3`_. + +iOS +... + +The following system frameworks will be used on iOS and iOS Simulator. + + - `Accelerate `_ to compute + FFTs and other vectorized operations optimally. + + - `CoreAudio `_ and + `AudioToolbox `_ to + decode audio from files and network streams. + +To build aubio for iOS, configure with ``--with-target-platform=ios``. For the +iOS Simulator, use ``--with-target-platform=iosimulator`` instead. + +By default, aubio is built with the following flags on iOS: + +.. code:: bash + + CFLAGS="-fembed-bitcode -arch arm64 -arch armv7 -arch armv7s -miphoneos-version-min=6.1" + +and on iOS Simulator: + +.. code:: + + CFLAGS="-arch i386 -arch x86_64 -mios-simulator-version-min=6.1" + +Set ``CFLAGS`` and ``LINKFLAGS`` to change these default values, or edit +``wscript`` directly. + +Other options +------------- + +Some additional options can be passed to the configure step. For the complete +list of options, run: + +.. code:: bash + + $ ./waf --help + +Here is an example of a custom command: + +.. code:: bash + + $ ./waf --verbose configure build install \ + --enable-avcodec --enable-wavread --disable-wavwrite \ + --enable-sndfile --enable-samplerate --enable-docs \ + --destdir $PWD/build/destdir --testcmd="echo %s" \ + --prefix=/opt --libdir=/opt/lib/multiarch \ + --manpagesdir=/opt/share/man \ + uninstall clean distclean dist distcheck + +Double precision +................ + +To compile aubio in double precision mode, configure with ``--enable-double``. + +To compile aubio in single precision mode, use ``--disable-double`` (default). + +Disabling the tests +................... + +In some case, for instance when cross-compiling, unit tests should not be run. +Option ``--notests`` can be used for this purpose. The tests will not be +executed, but the binaries will be compiled, ensuring that linking against +libaubio works as expected. + +.. note:: + + The ``--notests`` option should be passed to both ``build`` and ``install`` + targets, otherwise waf will try to run them. + +Edit wscript +............ + +Many of the options are gathered in the file `wscript`. a good starting point +when looking for additional options. + +.. _build_docs: + +Building the docs +----------------- + +If the following command line tools are found, the documentation will be built +built: + + - `doxygen `_ to build the :ref:`doxygen-documentation`. + - `txt2man `_ to build the :ref:`manpages` + - `sphinx `_ to build this document + +These tools are searched for in the current ``PATH`` environment variable. +By default, the documentation is built only if the tools are found. + +To disable the documentation, configure with ``--disable-docs``. To build with +the documentation, configure with ``--enable-docs``. diff --git a/doc/statuslinks.rst b/doc/statuslinks.rst new file mode 100644 index 00000000..d392f586 --- /dev/null +++ b/doc/statuslinks.rst @@ -0,0 +1,24 @@ +Current status +============== + +.. image:: https://travis-ci.org/aubio/aubio.svg?branch=master + :target: https://travis-ci.org/aubio/aubio + :alt: Travis build status + +.. image:: https://ci.appveyor.com/api/projects/status/f3lhy3a57rkgn5yi?svg=true + :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.4.svg?maxAge=2592000 + :target: https://github.com/aubio/aubio + :alt: Commits since last release + + diff --git a/doc/web.cfg b/doc/web.cfg index dc03fb76..0adeadd5 100644 --- a/doc/web.cfg +++ b/doc/web.cfg @@ -1,4 +1,4 @@ -# Doxyfile 1.8.5 +# Doxyfile 1.8.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -38,7 +38,7 @@ PROJECT_NAME = aubio # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "0.4.2~alpha" +PROJECT_NUMBER = "0.4.5~alpha" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -70,15 +70,25 @@ OUTPUT_DIRECTORY = web CREATE_SUBDIRS = NO +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- -# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, -# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, -# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, -# Turkish, Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English @@ -259,9 +269,12 @@ OPTIMIZE_OUTPUT_VHDL = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # @@ -500,6 +513,13 @@ HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -521,7 +541,8 @@ SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO @@ -659,8 +680,7 @@ LAYOUT_FILE = # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. +# search path. See also \cite for info how to create references. CITE_BIB_FILES = @@ -770,6 +790,7 @@ RECURSIVE = YES EXCLUDE = ../src/aubio_priv.h \ ../src/mathutils.h \ + ../src/io/ioutils.h \ ../src/io/audio_unit.h \ ../src/io/source_sndfile.h \ ../src/io/source_apple_audio.h \ @@ -978,6 +999,25 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1070,13 +1110,15 @@ HTML_FOOTER = HTML_STYLESHEET = -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra stylesheet files is of importance (e.g. the last +# stylesheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1241,7 +1283,8 @@ GENERATE_CHI = NO CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1441,7 +1484,7 @@ MATHJAX_FORMAT = HTML-CSS # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example @@ -1481,11 +1524,11 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. There -# are two flavours of web server based searching depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. See -# the section "External Indexing and Searching" for details. +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1613,17 +1656,19 @@ EXTRA_PACKAGES = # # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will -# replace them by respectively the title of the page, the current date and time, -# only the current date, the version number of doxygen, the project name (see -# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, +# for the replacement values of the other commands the user is refered to +# HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. # # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1647,7 +1692,7 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate # the PDF file directly from the LaTeX files. Set this option to YES to get a # higher quality PDF documentation. # The default value is: YES. @@ -1773,6 +1818,13 @@ MAN_OUTPUT = man MAN_EXTENSION = .3 +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without @@ -1800,18 +1852,6 @@ GENERATE_XML = NO XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify a XML DTD, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_DTD = - # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size @@ -1839,6 +1879,15 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook +# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -1958,9 +2007,9 @@ PREDEFINED = EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an -# all uppercase name, and do not end with a semicolon. Such function macros are -# typically used for boiler-plate code, and will confuse the parser if not +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1980,7 +2029,7 @@ SKIP_FUNCTION_MACROS = YES # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. -# Note: Each tag file must have an unique name (where the name does NOT include +# Note: Each tag file must have a unique name (where the name does NOT include # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. @@ -2040,6 +2089,13 @@ CLASS_DIAGRAMS = YES MSCGEN_PATH = +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + # If set to YES, the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2051,7 +2107,7 @@ HIDE_UNDOC_RELATIONS = YES # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. HAVE_DOT = NO @@ -2065,7 +2121,7 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font n the dot files that doxygen +# When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make # sure dot is able to find the font, which can be done by putting it in a # standard location or by setting the DOTFONTPATH environment variable or by @@ -2203,7 +2259,9 @@ DIRECTORY_GRAPH = YES # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd and svg. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2240,6 +2298,21 @@ DOTFILE_DIRS = MSCFILE_DIRS = +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. +# This tag requires that the tag HAVE_DOT is set to YES. + +PLANTUML_JAR_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized diff --git a/doc/xcode_frameworks.rst b/doc/xcode_frameworks.rst new file mode 100644 index 00000000..e665cfeb --- /dev/null +++ b/doc/xcode_frameworks.rst @@ -0,0 +1,70 @@ +.. _xcode-frameworks-label: + +Using aubio frameworks in Xcode +------------------------------- + +`Binary frameworks`_ are available and ready to use in your XCode project, for +`iOS`_ and `macOS`_. + +#. Download and extract the corresponding ``framework.zip`` file from the `Download`_ page + +#. Select **Build Phases** in your project setting and unfold **Link Binary with Libraries** + +#. Add *AudioToolbox* and *Accelerate* system frameworks (or make sure they are listed) + +#. Add ``aubio.framework`` from the unzipped ``framework.zip`` + +#. Include the aubio header in your code: + + * in C/C++: + + .. code-block:: c + + #include + + * in Obj-C: + + .. code-block:: obj-c + + #import + + * in Swift: + + .. code-block:: swift + + import aubio + +Using aubio from swift +...................... + +Here is a short example showing how to read a sound file in swift: + + + .. code-block:: swift + + import aubio + + let path = Bundle.main.path(forResource: "example", ofType: "mp4") + if (path != nil) { + let hop_size : uint_t = 512 + let a = new_fvec(hop_size) + let b = new_aubio_source(path, 0, hop_size) + var read: uint_t = 0 + var total_frames : uint_t = 0 + while (true) { + aubio_source_do(b, a, &read) + total_frames += read + if (read < hop_size) { break } + } + print("read", total_frames, "frames at", aubio_source_get_samplerate(b), "Hz") + del_aubio_source(b) + del_fvec(a) + } else { + print("could not find file") + } + + +.. _Binary frameworks: https://aubio.org/download +.. _iOS: https://aubio.org/download#ios +.. _macOS: https://aubio.org/download#osx +.. _Download: https://aubio.org/download diff --git a/examples/aubiomfcc.c b/examples/aubiomfcc.c index d8bb9102..0942ade7 100644 --- a/examples/aubiomfcc.c +++ b/examples/aubiomfcc.c @@ -48,6 +48,7 @@ void process_print (void) } int main(int argc, char **argv) { + int ret = 0; // change some default params buffer_size = 512; hop_size = 256; @@ -62,6 +63,10 @@ int main(int argc, char **argv) { fftgrain = new_cvec (buffer_size); mfcc = new_aubio_mfcc(buffer_size, n_filters, n_coefs, samplerate); mfcc_out = new_fvec(n_coefs); + if (pv == NULL || fftgrain == NULL || mfcc == NULL || mfcc_out == NULL) { + ret = 1; + goto beach; + } examples_common_process((aubio_process_func_t)process_block, process_print); @@ -70,7 +75,7 @@ int main(int argc, char **argv) { del_aubio_mfcc(mfcc); del_fvec(mfcc_out); +beach: examples_common_del(); - return 0; + return ret; } - diff --git a/examples/aubionotes.c b/examples/aubionotes.c index 544bdd8b..f74063ae 100644 --- a/examples/aubionotes.c +++ b/examples/aubionotes.c @@ -18,80 +18,29 @@ */ -#define AUBIO_UNSTABLE 1 // for fvec_median #include "utils.h" #define PROG_HAS_PITCH 1 #define PROG_HAS_ONSET 1 +#define PROG_HAS_SILENCE 1 #define PROG_HAS_JACK 1 // TODO add PROG_HAS_OUTPUT #include "parse_args.h" -uint_t median = 6; - -fvec_t *note_buffer; -fvec_t *note_buffer2; - -smpl_t curnote = 0.; -smpl_t newnote = 0.; -uint_t isready = 0; - -aubio_pitch_t *pitch; -aubio_onset_t *o; -fvec_t *onset; -fvec_t *pitch_obuf; - -/** append new note candidate to the note_buffer and return filtered value. we - * need to copy the input array as fvec_median destroy its input data.*/ -void note_append (fvec_t * note_buffer, smpl_t curnote); -uint_t get_note (fvec_t * note_buffer, fvec_t * note_buffer2); +aubio_notes_t *notes; +smpl_t lastmidi = 0.; void process_block (fvec_t *ibuf, fvec_t *obuf) { - smpl_t new_pitch, curlevel; - fvec_zeros(obuf); - aubio_onset_do(o, ibuf, onset); - - aubio_pitch_do (pitch, ibuf, pitch_obuf); - new_pitch = fvec_get_sample(pitch_obuf, 0); - if(median){ - note_append(note_buffer, new_pitch); + aubio_notes_do (notes, ibuf, obuf); + // did we get a note off? + if (obuf->data[2] != 0) { + lastmidi = obuf->data[2]; + send_noteon(lastmidi, 0); } - - /* curlevel is negatif or 1 if silence */ - curlevel = aubio_level_detection(ibuf, silence_threshold); - if (fvec_get_sample(onset, 0)) { - /* test for silence */ - if (curlevel == 1.) { - if (median) isready = 0; - /* send note off */ - send_noteon(curnote,0); - } else { - if (median) { - isready = 1; - } else { - /* kill old note */ - send_noteon(curnote,0); - /* get and send new one */ - send_noteon(new_pitch,127+(int)floor(curlevel)); - curnote = new_pitch; - } - } - } else { - if (median) { - if (isready > 0) - isready++; - if (isready == median) - { - /* kill old note */ - send_noteon(curnote,0); - newnote = get_note(note_buffer, note_buffer2); - curnote = newnote; - /* get and send new one */ - if (curnote>45){ - send_noteon(curnote,127+(int)floor(curlevel)); - } - } - } // if median + // did we get a note on? + if (obuf->data[0] != 0) { + lastmidi = obuf->data[0]; + send_noteon(lastmidi, obuf->data[1]); } } @@ -100,28 +49,9 @@ void process_print (void) //if (verbose) outmsg("%f\n",pitch_obuf->data[0]); } -void -note_append (fvec_t * note_buffer, smpl_t curnote) -{ - uint_t i = 0; - for (i = 0; i < note_buffer->length - 1; i++) { - note_buffer->data[i] = note_buffer->data[i + 1]; - } - note_buffer->data[note_buffer->length - 1] = curnote; - return; -} - -uint_t -get_note (fvec_t * note_buffer, fvec_t * note_buffer2) -{ - uint_t i; - for (i = 0; i < note_buffer->length; i++) { - note_buffer2->data[i] = note_buffer->data[i]; - } - return fvec_median (note_buffer2); -} - int main(int argc, char **argv) { + int ret = 0; + examples_common_init(argc,argv); verbmsg ("using source: %s at %dHz\n", source_uri, samplerate); @@ -136,32 +66,33 @@ int main(int argc, char **argv) { verbmsg ("hop_size: %d, ", hop_size); verbmsg ("tolerance: %f\n", pitch_tolerance); - o = new_aubio_onset (onset_method, buffer_size, hop_size, samplerate); - if (onset_threshold != 0.) aubio_onset_set_threshold (o, onset_threshold); - onset = new_fvec (1); - - pitch = new_aubio_pitch (pitch_method, buffer_size * 4, hop_size, samplerate); - if (pitch_tolerance != 0.) aubio_pitch_set_tolerance (pitch, pitch_tolerance); - pitch_obuf = new_fvec (1); + notes = new_aubio_notes ("default", buffer_size, hop_size, samplerate); + if (notes == NULL) { ret = 1; goto beach; } - if (median) { - note_buffer = new_fvec (median); - note_buffer2 = new_fvec (median); + if (onset_minioi != 0.) { + aubio_notes_set_minioi_ms(notes, onset_minioi); + } + if (onset_threshold != 0.) { + errmsg ("warning: onset threshold not supported yet\n"); + //aubio_onset_set_threshold(aubio_notes_get_aubio_onset(o), onset_threshold); + } + if (silence_threshold != -90.) { + if (aubio_notes_set_silence (notes, silence_threshold) != 0) { + errmsg ("failed setting notes silence threshold to %.2f\n", + silence_threshold); + } } examples_common_process((aubio_process_func_t)process_block, process_print); - // send a last note off - send_noteon (curnote, 0); - - del_aubio_pitch (pitch); - if (median) { - del_fvec (note_buffer); - del_fvec (note_buffer2); + // send a last note off if required + if (lastmidi) { + send_noteon (lastmidi, 0); } - del_fvec (pitch_obuf); + del_aubio_notes (notes); + +beach: examples_common_del(); - return 0; + return ret; } - diff --git a/examples/aubioonset.c b/examples/aubioonset.c index 8f30bad1..032eb004 100644 --- a/examples/aubioonset.c +++ b/examples/aubioonset.c @@ -21,6 +21,7 @@ #include "utils.h" #define PROG_HAS_ONSET 1 #define PROG_HAS_OUTPUT 1 +#define PROG_HAS_SILENCE 1 #define PROG_HAS_JACK 1 #include "parse_args.h" @@ -37,6 +38,8 @@ void process_block(fvec_t *ibuf, fvec_t *obuf) fvec_zeros(obuf); if ( is_onset ) { aubio_wavetable_play ( wavetable ); + /* send a midi tap (default to C0) out to the midi output */ + if (usejack) send_noteon(miditap_note, miditap_velo); } else { aubio_wavetable_stop ( wavetable ); } @@ -55,6 +58,7 @@ void process_print (void) } int main(int argc, char **argv) { + int ret = 0; examples_common_init(argc,argv); verbmsg ("using source: %s at %dHz\n", source_uri, samplerate); @@ -65,10 +69,13 @@ int main(int argc, char **argv) { verbmsg ("threshold: %f\n", onset_threshold); o = new_aubio_onset (onset_method, buffer_size, hop_size, samplerate); + if (o == NULL) { ret = 1; goto beach; } if (onset_threshold != 0.) aubio_onset_set_threshold (o, onset_threshold); if (silence_threshold != -90.) aubio_onset_set_silence (o, silence_threshold); + if (onset_minioi != 0.) + aubio_onset_set_minioi_s (o, onset_minioi); onset = new_fvec (1); @@ -78,10 +85,16 @@ int main(int argc, char **argv) { examples_common_process((aubio_process_func_t)process_block, process_print); + // send a last note off + if (usejack) { + send_noteon (miditap_note, 0); + } + del_aubio_onset (o); del_aubio_wavetable (wavetable); del_fvec (onset); +beach: examples_common_del(); - return 0; + return ret; } diff --git a/examples/aubiopitch.c b/examples/aubiopitch.c index bdda9508..2d7a0122 100644 --- a/examples/aubiopitch.c +++ b/examples/aubiopitch.c @@ -21,6 +21,7 @@ #include "utils.h" #define PROG_HAS_PITCH 1 #define PROG_HAS_OUTPUT 1 +#define PROG_HAS_SILENCE 1 #define PROG_HAS_JACK 1 #include "parse_args.h" @@ -51,6 +52,7 @@ void process_print (void) } int main(int argc, char **argv) { + int ret = 0; buffer_size = 2048; @@ -64,6 +66,7 @@ int main(int argc, char **argv) { verbmsg ("tolerance: %f\n", pitch_tolerance); o = new_aubio_pitch (pitch_method, buffer_size, hop_size, samplerate); + if (o == NULL) { ret = 1; goto beach; } if (pitch_tolerance != 0.) aubio_pitch_set_tolerance (o, pitch_tolerance); if (silence_threshold != -90.) @@ -82,7 +85,7 @@ int main(int argc, char **argv) { del_aubio_wavetable (wavetable); del_fvec (pitch); +beach: examples_common_del(); - return 0; + return ret; } - diff --git a/examples/aubioquiet.c b/examples/aubioquiet.c index bbe158b4..f62e1ed6 100644 --- a/examples/aubioquiet.c +++ b/examples/aubioquiet.c @@ -19,6 +19,7 @@ */ #include "utils.h" +#define PROG_HAS_SILENCE 1 #include "parse_args.h" sint_t wassilence = 1, issilence; diff --git a/examples/aubiotrack.c b/examples/aubiotrack.c index 330fc8ca..36267cf6 100644 --- a/examples/aubiotrack.c +++ b/examples/aubiotrack.c @@ -20,6 +20,8 @@ #include "utils.h" #define PROG_HAS_TEMPO 1 +#define PROG_HAS_ONSET 1 +#define PROG_HAS_SILENCE 1 #define PROG_HAS_OUTPUT 1 #define PROG_HAS_JACK 1 #include "parse_args.h" @@ -27,8 +29,8 @@ aubio_tempo_t * tempo; aubio_wavetable_t *wavetable; fvec_t * tempo_out; -smpl_t is_beat = 0; -uint_t is_silence = 0.; +smpl_t is_beat = 0.; +uint_t is_silence = 0; void process_block(fvec_t * ibuf, fvec_t *obuf) { aubio_tempo_do (tempo, ibuf, tempo_out); @@ -39,6 +41,8 @@ void process_block(fvec_t * ibuf, fvec_t *obuf) { fvec_zeros (obuf); if ( is_beat && !is_silence ) { aubio_wavetable_play ( wavetable ); + /* send a midi tap (default to C0) out to the midi output */ + if (usejack) send_noteon(miditap_note, miditap_velo); } else { aubio_wavetable_stop ( wavetable ); } @@ -56,6 +60,7 @@ void process_print (void) { } int main(int argc, char **argv) { + int ret = 0; // override general settings from utils.c buffer_size = 1024; hop_size = 512; @@ -71,9 +76,11 @@ int main(int argc, char **argv) { tempo_out = new_fvec(2); tempo = new_aubio_tempo(tempo_method, buffer_size, hop_size, samplerate); + if (tempo == NULL) { ret = 1; goto beach; } // set silence threshold very low to output beats even during silence // aubio_tempo_set_silence(tempo, -1000.); if (onset_threshold != 0.) aubio_tempo_set_threshold (tempo, onset_threshold); + if (onset_minioi != 0.) errmsg ("warning: minioio not supported yet\n"); wavetable = new_aubio_wavetable (samplerate, hop_size); aubio_wavetable_set_freq ( wavetable, 2450.); @@ -81,11 +88,16 @@ int main(int argc, char **argv) { examples_common_process((aubio_process_func_t)process_block,process_print); + // send a last note off + if (usejack) { + send_noteon (miditap_note, 0); + } + del_aubio_tempo(tempo); del_aubio_wavetable (wavetable); del_fvec(tempo_out); +beach: examples_common_del(); - return 0; + return ret; } - diff --git a/examples/jackio.c b/examples/jackio.c index 475471fd..f781fb16 100644 --- a/examples/jackio.c +++ b/examples/jackio.c @@ -21,7 +21,7 @@ #include #include "config.h" -#if HAVE_JACK +#ifdef HAVE_JACK #include "utils.h" // for aubio_process_func_t #include "jackio.h" #include "aubio_priv.h" diff --git a/examples/parse_args.h b/examples/parse_args.h index 0548038a..f46629f7 100644 --- a/examples/parse_args.h +++ b/examples/parse_args.h @@ -18,6 +18,12 @@ */ +#include "config.h" + +#ifdef HAVE_GETOPT_H +#include +#endif + extern int verbose; // input / output extern int usejack; @@ -30,6 +36,7 @@ extern uint_t hop_size; // onset stuff extern char_t * onset_method; extern smpl_t onset_threshold; +extern smpl_t onset_minioi; // pitch stuff extern char_t * pitch_method; extern char_t * pitch_unit; @@ -41,6 +48,9 @@ extern char_t * tempo_method; // more general stuff extern smpl_t silence_threshold; extern uint_t mix_input; +// midi tap +extern smpl_t miditap_note; +extern smpl_t miditap_velo; extern uint_t force_overwrite; @@ -63,6 +73,7 @@ void usage (FILE * stream, int exit_code); void usage (FILE * stream, int exit_code) { +#ifdef HAVE_GETOPT_H fprintf (stream, "usage: %s [ options ] \n", prog_name); fprintf (stream, " -i --input input file\n" @@ -81,6 +92,8 @@ void usage (FILE * stream, int exit_code) " default=hfc\n" " -t --onset-threshold set onset detection threshold\n" " a value between 0.1 (more detections) and 1 (less); default=0.3\n" + " -M --minioi set minimum inter-onset interval\n" + " a value in second; default=0.012\n" #endif /* PROG_HAS_ONSET */ #ifdef PROG_HAS_PITCH " -p --pitch select pitch detection algorithm\n" @@ -90,8 +103,10 @@ void usage (FILE * stream, int exit_code) " -l --pitch-tolerance select pitch tolerance\n" " (yin, yinfft only) a value between 0.1 and 0.7; default=0.3\n" #endif /* PROG_HAS_PITCH */ +#ifdef PROG_HAS_SILENCE " -s --silence select silence threshold\n" " a value in dB, for instance -70, or -100; default=-90\n" +#endif /* PROG_HAS_SILENCE */ " -T --time-format select time values output format\n" " (samples, ms, seconds) default=seconds\n" #ifdef PROG_HAS_OUTPUT @@ -99,35 +114,53 @@ void usage (FILE * stream, int exit_code) " input signal will be added to output synthesis\n" " -f --force-overwrite overwrite output file if needed\n" " do not fail if output file already exists\n" -#endif +#endif /* PROG_HAS_OUTPUT */ #ifdef PROG_HAS_JACK " -j --jack use Jack\n" -#endif +#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 */ " -v --verbose be verbose\n" " -h --help display this message\n" ); +#else /* HAVE_GETOPT_H */ + fprintf (stream, "warning: compiled with getopt.h, no argument parsing\n"); + fprintf (stream, "usage: %s \n", prog_name); +#endif /* HAVE_GETOPT_H */ exit (exit_code); } int parse_args (int argc, char **argv) { +#ifdef HAVE_GETOPT_H const char *options = "hv" "i:r:B:H:" #ifdef PROG_HAS_JACK "j" +#if defined(PROG_HAS_ONSET) && !defined(PROG_HAS_PITCH) + "N:V:" +#endif /* defined(PROG_HAS_ONSET) && !defined(PROG_HAS_PITCH) */ #endif /* PROG_HAS_JACK */ #ifdef PROG_HAS_OUTPUT "o:" #endif /* PROG_HAS_OUTPUT */ #ifdef PROG_HAS_ONSET - "O:t:" + "O:t:M:" #endif /* PROG_HAS_ONSET */ #ifdef PROG_HAS_PITCH "p:u:l:" #endif /* PROG_HAS_PITCH */ "T:" - "s:mf"; +#ifdef PROG_HAS_SILENCE + "s:" +#endif /* PROG_HAS_SILENCE */ +#ifdef PROG_HAS_OUTPUT + "mf" +#endif /* PROG_HAS_OUTPUT */ + ; int next_option; struct option long_options[] = { {"help", 0, NULL, 'h'}, @@ -138,6 +171,10 @@ parse_args (int argc, char **argv) {"hopsize", 1, NULL, 'H'}, #ifdef PROG_HAS_JACK {"jack", 0, NULL, 'j'}, +#if defined(PROG_HAS_ONSET) && !defined(PROG_HAS_PITCH) + {"miditap-note", 1, NULL, 'N'}, + {"miditap-velo", 1, NULL, 'V'}, +#endif /* PROG_HAS_ONSET !PROG_HAS_PITCH */ #endif /* PROG_HAS_JACK */ #ifdef PROG_HAS_OUTPUT {"output", 1, NULL, 'o'}, @@ -145,23 +182,30 @@ parse_args (int argc, char **argv) #ifdef PROG_HAS_ONSET {"onset", 1, NULL, 'O'}, {"onset-threshold", 1, NULL, 't'}, + {"onset-minioi", 1, NULL, 'M'}, #endif /* PROG_HAS_ONSET */ #ifdef PROG_HAS_PITCH {"pitch", 1, NULL, 'p'}, {"pitch-unit", 1, NULL, 'u'}, {"pitch-tolerance", 1, NULL, 'l'}, #endif /* PROG_HAS_PITCH */ +#ifdef PROG_HAS_SILENCE {"silence", 1, NULL, 's'}, +#endif /* PROG_HAS_SILENCE */ {"time-format", 1, NULL, 'T'}, +#ifdef PROG_HAS_OUTPUT {"mix-input", 0, NULL, 'm'}, {"force-overwrite", 0, NULL, 'f'}, +#endif /* PROG_HAS_OUTPUT */ {NULL, 0, NULL, 0} }; +#endif /* HAVE_GETOPT_H */ prog_name = argv[0]; if (argc < 1) { usage (stderr, 1); return -1; } +#ifdef HAVE_GETOPT_H do { next_option = getopt_long (argc, argv, options, long_options, NULL); switch (next_option) { @@ -174,6 +218,12 @@ parse_args (int argc, char **argv) case 'j': usejack = 1; break; + case 'N': + miditap_note = (smpl_t) atoi (optarg); + break; + case 'V': + miditap_velo = (smpl_t) atoi (optarg); + break; case 'i': source_uri = optarg; break; @@ -198,6 +248,9 @@ parse_args (int argc, char **argv) case 't': /* threshold value for onset */ onset_threshold = (smpl_t) atof (optarg); break; + case 'M': /* minimum inter-onset-interval */ + onset_minioi = (smpl_t) atof (optarg); + break; case 'p': pitch_method = optarg; break; @@ -235,6 +288,9 @@ parse_args (int argc, char **argv) } } while (next_option != -1); +#else /* HAVE_GETOPT_H */ + int optind = 1; +#endif /* HAVE_GETOPT_H */ // if unique, use the non option argument as the source if ( source_uri == NULL ) { diff --git a/examples/utils.c b/examples/utils.c index a28f4097..39adef9d 100644 --- a/examples/utils.c +++ b/examples/utils.c @@ -43,6 +43,7 @@ uint_t hop_size = 256; // onset stuff char_t * onset_method = "default"; smpl_t onset_threshold = 0.0; // will be set if != 0. +smpl_t onset_minioi = 0.0; // will be set if != 0. // pitch stuff char_t * pitch_unit = "default"; char_t * pitch_method = "default"; @@ -64,6 +65,9 @@ aubio_sink_t *this_sink = NULL; fvec_t *ibuf; fvec_t *obuf; +smpl_t miditap_note = 69.; +smpl_t miditap_velo = 65.; + /* settings */ int blocks = 0; @@ -72,7 +76,8 @@ extern int parse_args (int argc, char **argv); #if HAVE_JACK aubio_jack_t *jack_setup; -#endif +jack_midi_event_t ev; +#endif /* HAVE_JACK */ void examples_common_init (int argc, char **argv); void examples_common_del (void); @@ -114,7 +119,7 @@ void examples_common_init (int argc, char **argv) jack_setup = new_aubio_jack (hop_size, 1, 1, 0, 1); samplerate = aubio_jack_get_samplerate (jack_setup); source_uri = "jack"; -#endif +#endif /* HAVE_JACK */ } ibuf = new_fvec (hop_size); obuf = new_fvec (hop_size); @@ -123,6 +128,9 @@ void examples_common_init (int argc, char **argv) void examples_common_del (void) { +#ifdef HAVE_JACK + if (ev.buffer) free(ev.buffer); +#endif del_fvec (ibuf); del_fvec (obuf); aubio_cleanup (); @@ -137,16 +145,19 @@ void examples_common_process (aubio_process_func_t process_func, uint_t read = 0; if (usejack) { -#if HAVE_JACK +#ifdef HAVE_JACK + ev.size = 3; + ev.buffer = malloc (3 * sizeof (jack_midi_data_t)); + ev.time = 0; // send it now debug ("Jack activation ...\n"); aubio_jack_activate (jack_setup, process_func); debug ("Processing (Ctrl+C to quit) ...\n"); pause (); aubio_jack_close (jack_setup); -#else +#else /* HAVE_JACK */ usage (stderr, 1); outmsg ("Compiled without jack output, exiting.\n"); -#endif +#endif /* HAVE_JACK */ } else { @@ -178,17 +189,12 @@ void examples_common_process (aubio_process_func_t process_func, } void -send_noteon (int pitch, int velo) +send_noteon (smpl_t pitch, smpl_t velo) { - smpl_t mpitch = floor (aubio_freqtomidi (pitch) + .5); -#if HAVE_JACK - jack_midi_event_t ev; - ev.size = 3; - ev.buffer = malloc (3 * sizeof (jack_midi_data_t)); // FIXME - ev.time = 0; +#ifdef HAVE_JACK if (usejack) { ev.buffer[2] = velo; - ev.buffer[1] = mpitch; + ev.buffer[1] = pitch; if (velo == 0) { ev.buffer[0] = 0x80; /* note off */ } else { @@ -201,7 +207,7 @@ send_noteon (int pitch, int velo) print_time (blocks * hop_size); outmsg ("\n"); } else { - outmsg ("%f\t", mpitch); + outmsg ("%f\t", pitch); print_time (blocks * hop_size); outmsg ("\t"); } diff --git a/examples/utils.h b/examples/utils.h index 93b0e18b..e911bef4 100644 --- a/examples/utils.h +++ b/examples/utils.h @@ -18,16 +18,30 @@ */ -#include -#include -#include -#include -#include -#include /* for isfinite */ -#include /* for strcmp */ #include + #include "config.h" +#ifdef HAVE_STDIO_H +#include // for fprintf +#endif +#ifdef HAVE_STDLIB_H +#include // for exit +#endif +#ifdef HAVE_UNISTD_H +#include // for access +#elif defined(HAVE_WIN_HACKS) +#include +#define access _access +#define F_OK 0 +#endif +#ifdef HAVE_MATH_H +#include // for isfinite +#endif +#ifdef HAVE_STRING_H +#include // for strcmp +#endif + #ifdef HAVE_C99_VARARGS_MACROS #ifdef HAVE_DEBUG #define debug(...) fprintf (stderr, __VA_ARGS__) @@ -49,7 +63,7 @@ #endif typedef void (aubio_print_func_t) (void); -void send_noteon (int pitch, int velo); +void send_noteon (smpl_t pitch, smpl_t velo); /** common process function */ typedef int (*aubio_process_func_t) (fvec_t * input, fvec_t * output); diff --git a/examples/wscript_build b/examples/wscript_build index 2ef9709c..7970bf65 100644 --- a/examples/wscript_build +++ b/examples/wscript_build @@ -1,33 +1,27 @@ # vim:set syntax=python: -uselib = [] -uselib += ['FFTW3', 'FFTW3F'] -uselib += ['SAMPLERATE'] -uselib += ['SNDFILE'] -uselib += ['AVCODEC'] -uselib += ['AVFORMAT'] -uselib += ['AVRESAMPLE'] -uselib += ['AVUTIL'] +import os.path + +uselib = ['aubio'] uselib += ['JACK'] -uselib += ['BLAS'] +includes = ['../src'] utils_source = ['utils.c', 'jackio.c'] programs_source = ctx.path.ant_glob('*.c', excl = utils_source) # build examples bld(features = 'c', source = utils_source, - includes = ['../src'], - uselib = uselib, + includes = includes, + use = uselib, target = 'utilsio') # loop over all *.c filenames in examples to build them all for source_file in programs_source: + target = os.path.basename(os.path.splitext(str(source_file))[0]) bld(features = 'c cprogram', - includes = '../src', - lib = 'm', - use = ['aubio', 'utilsio'], - uselib = uselib, source = source_file, - target = str(source_file).split('.')[0] - ) + target = target, + includes = includes, + use = uselib + ['utilsio'], + ) diff --git a/nose2.cfg b/nose2.cfg new file mode 100644 index 00000000..d1be6d8f --- /dev/null +++ b/nose2.cfg @@ -0,0 +1,6 @@ +[unittest] +start-dir = python/tests/ +plugins = nose2.plugins.mp + +[multiprocess] +always-on = false diff --git a/python/MANIFEST.in b/python/MANIFEST.in deleted file mode 100644 index cffccebf..00000000 --- a/python/MANIFEST.in +++ /dev/null @@ -1,8 +0,0 @@ -include README COPYING VERSION -include ext/*.h -include lib/generator.py -include lib/gen_pyobject.py -include gen/aubio-generated.h -include tests/run_all_tests -include tests/*.py -include demos/*.py diff --git a/python/README b/python/README deleted file mode 100644 index c49d2a78..00000000 --- a/python/README +++ /dev/null @@ -1,73 +0,0 @@ -Python aubio module -=================== - -This module wraps the aubio library for Python using the numpy module. - -Before compiling this module, you must have compiled libaubio. - -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 ----------------------- - -After libaubio has been build successfully, and provided Python development -headers and numpy can be found on your system, you should be able to build the -aubio Python module: - - $ ./setup.py build - -To find out more about `setup.py` options: - - $ ./setup.py --help - -Installing ----------- - -To install the Python module: - - $ ./setup.py install - -Using the Python module ------------------------ - -Once the aubio library and the Python module are installed, you will be able to -import the aubio module: - - $ python - [...] - >>> import aubio - >>> - -Alternatively, you may want to use the Python module without installing it by -setting PYTHONPATH: - - $ export PYTHONPATH=$PYTHONPATH:$PWD/`ls -rtd build/lib.* | head -1`:$PWD/tests - -Similarly, you can use the aubio module without installing libaubio by pointing -LD_LIBRARY_PATH to the path libaubio can be found at: - - $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:PWD/../build/src - -Or on Mac OS X systems, setting DYLD_LIBRARY_PATH: - - $ export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$PWD/../build/src - -Testing the Python module -------------------------- - -Once both the C library and the Python module have been built correctly, and -after you have installed them (or setting the environment variable correctly), -you should be able to run the tests: - - $ ./tests/run_all_tests - -And to try out the demos: - - $ ./demos/demo_source.wav /path/to/sound/sample.wav - -You need to install additional modules to run some of the demos. For -instance, several demos use [matplotlib](http://matplotlib.org/) to draw plots. -Some more demos use [PySoundCard](https://github.com/bastibe/PySoundCard) to -play and record sounds. diff --git a/python/README.md b/python/README.md new file mode 100644 index 00000000..dbb8ff37 --- /dev/null +++ b/python/README.md @@ -0,0 +1,104 @@ +Python aubio module +=================== + +This module wraps the aubio library for Python using the numpy module. + +Using the Python aubio module +----------------------------- + +After installing python-aubio, you will be able to import the aubio module: + + $ python + [...] + >>> import aubio + >>> help(aubio.miditofreq) + +Finding some inspiration +------------------------ + +Some examples are available in the `python/demos` directory. These scripts are +small programs written in python and using python-aubio. + +For instance, `demo_source.py` reads a media file. + + $ ./python/demos/demo_source.py /path/to/sound/sample.wav + +and `demo_timestretch_online.py` stretches the original file into a new one: + + $ ./python/demo/demo_timestretch_online.py loop.wav stretched_loop.wav 0.92` + +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. + +Testing the Python module +------------------------- + +Python tests are in `python/tests` and use the [nose2 python package][nose2]. + +To run the all the python tests, use the script: + + $ ./python/tests/run_all_tests + +Each test script can also be called one at a time. For instance: + + $ ./python/tests/test_note2midi.py -v + +[nose2]: https://github.com/nose-devs/nose2 + +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 +---------- + +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 + diff --git a/python/VERSION b/python/VERSION deleted file mode 100644 index ff49ec1e..00000000 --- a/python/VERSION +++ /dev/null @@ -1,7 +0,0 @@ -AUBIO_MAJOR_VERSION=0 -AUBIO_MINOR_VERSION=4 -AUBIO_PATCH_VERSION=2 -AUBIO_VERSION_STATUS='~alpha' -LIBAUBIO_LT_CUR=4 -LIBAUBIO_LT_REV=1 -LIBAUBIO_LT_AGE=1 diff --git a/python/__init__.py b/python/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/demos/__init__.py b/python/demos/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/demos/demo_a_weighting.py b/python/demos/demo_a_weighting.py deleted file mode 100755 index 345e6510..00000000 --- a/python/demos/demo_a_weighting.py +++ /dev/null @@ -1,27 +0,0 @@ -#! /usr/bin/env python - - -def apply_filter(path, params = {}): - from aubio import source, sink, digital_filter - from os.path import basename, splitex, splitextt - s = source(path) - f = digital_filter(7) - f.set_a_weighting(s.samplerate) - #f = digital_filter(3) - #f.set_biquad(...) - o = sink("filtered_" + splitext(basename(path))[0] + ".wav") - # Total number of frames read - total_frames = 0 - - while True: - samples, read = s() - filtered_samples = f(samples) - o(samples, read) - total_frames += read - if read < s.hop_size: break - print "filtered", s.uri, "to", o.uri, "using an A-weighting filter" - -if __name__ == '__main__': - import sys - for f in sys.argv[1:]: - apply_filter(f) diff --git a/python/demos/demo_alsa.py b/python/demos/demo_alsa.py new file mode 100755 index 00000000..cd58a338 --- /dev/null +++ b/python/demos/demo_alsa.py @@ -0,0 +1,45 @@ +#! /usr/bin/env python + +import alsaaudio +import numpy as np +import aubio + +# constants +samplerate = 44100 +win_s = 2048 +hop_s = win_s // 2 +framesize = hop_s + +# set up audio input +recorder = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE) +recorder.setperiodsize(framesize) +recorder.setrate(samplerate) +recorder.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE) +recorder.setchannels(1) + +# create aubio pitch detection (first argument is method, "default" is +# "yinfft", can also be "yin", "mcomb", fcomb", "schmitt"). +pitcher = aubio.pitch("default", win_s, hop_s, samplerate) +# set output unit (can be 'midi', 'cent', 'Hz', ...) +pitcher.set_unit("Hz") +# ignore frames under this level (dB) +pitcher.set_silence(-40) + +print("Starting to listen, press Ctrl+C to stop") + +# main loop +while True: + try: + # read data from audio input + _, data = recorder.read() + # convert data to aubio float samples + samples = np.fromstring(data, dtype=aubio.float_type) + # pitch of current frame + freq = pitcher(samples)[0] + # compute energy of current block + energy = np.sum(samples**2)/len(samples) + # do something with the results + print("{:10.4f} {:10.4f}".format(freq,energy)) + except KeyboardInterrupt: + print("Ctrl+C pressed, exiting") + break diff --git a/python/demos/demo_bench_yin.py b/python/demos/demo_bench_yin.py new file mode 100755 index 00000000..0d03c05c --- /dev/null +++ b/python/demos/demo_bench_yin.py @@ -0,0 +1,50 @@ +#! /usr/bin/env python + +import numpy as np +from aubio import pitch +import pylab as plt + +buf_size = 2048 * 1 +hop_size = buf_size // 4 + +samplerate = 44100 +minfreq = 40 +maxfreq = 6000 + +def sinewave(freq, duration, samplerate = samplerate): + """ generate a sinewave """ + length = hop_size + while length < duration * samplerate: + length += hop_size + return np.sin( 2. * np.pi * np.arange(length) * freq / samplerate ).astype("float32") + +def get_stats_for_pitch_method(method, freqs, samplerate = samplerate): + """ for a given pitch method and a list of frequency, generate a sinewave + and get mean deviation """ + means = np.zeros(len(freqs)) + medians = np.zeros(len(freqs)) + for freq, fn in zip(freqs, range(len(freqs))): + s = sinewave(freq, .50).reshape(-1, hop_size) + #s = (sinewave(freq, .50) + .0*sinewave(freq/2., .50)).reshape(-1, hop_size) + p = pitch(method, buf_size, hop_size, samplerate = samplerate) + candidates = np.zeros(len(s)) + #samples = np.zeros(buf_size) + for frame, i in zip(s, range(len(s))): + candidates[i] = p(frame)[0] + # skip first few candidates + candidates = candidates[4:] + means[fn] = np.mean(candidates[candidates != 0] - freq) + medians[fn] = np.median(candidates[candidates != 0] - freq) + print (freq, means[fn], medians[fn]) + return means, medians + +if __name__ == '__main__': + freqs = np.arange(minfreq, maxfreq, 1.) + modes = ["yin", "yinfft"] + for mode in modes: + means, medians = get_stats_for_pitch_method(mode, freqs) + plt.figure() + plt.plot(freqs, means, 'g-') + plt.plot(freqs, medians, 'r--') + #plt.savefig(mode + '_deviations_test.png', dpi=300) + plt.show() diff --git a/python/demos/demo_bpm_extract.py b/python/demos/demo_bpm_extract.py index 65b1c732..ba7fbad7 100755 --- a/python/demos/demo_bpm_extract.py +++ b/python/demos/demo_bpm_extract.py @@ -3,16 +3,18 @@ from aubio import source, tempo from numpy import median, diff -def get_file_bpm(path, params = {}): +def get_file_bpm(path, params = None): """ Calculate the beats per minute (bpm) of a given file. path: path to the file param: dictionary of parameters """ + if params is None: + params = {} try: win_s = params['win_s'] samplerate = params['samplerate'] hop_s = params['hop_s'] - except: + except KeyError: """ # super fast samplerate, win_s, hop_s = 4000, 128, 64 @@ -43,12 +45,18 @@ def get_file_bpm(path, params = {}): break # Convert to periods and to bpm - bpms = 60./diff(beats) - b = median(bpms) + if len(beats) > 1: + if len(beats) < 4: + print("few beats found in {:s}".format(path)) + bpms = 60./diff(beats) + b = median(bpms) + else: + b = 0 + print("not enough beats found in {:s}".format(path)) return b if __name__ == '__main__': import sys for f in sys.argv[1:]: bpm = get_file_bpm(f) - print "%6s" % ("%.2f" % bpm), f + print("{:6s} {:s}".format("{:2f}".format(bpm), f)) diff --git a/python/demos/demo_create_test_sounds.py b/python/demos/demo_create_test_sounds.py new file mode 100755 index 00000000..3d2f2d06 --- /dev/null +++ b/python/demos/demo_create_test_sounds.py @@ -0,0 +1,50 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +import sys, os +import numpy as np +from aubio import fvec, sink, float_type + +if __name__ == '__main__': + if len(sys.argv) < 1: + print('usage: %s' % sys.argv[0]) + sys.exit(1) + + samplerate = 44100 + hop_size = 256 + + # create python/tests/sounds if needed + output_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + output_dir = os.path.join(output_dir, 'tests', 'sounds') + if not os.path.isdir(output_dir): + os.makedirs(output_dir) + + filenames = ['44100Hz_1f_silence.wav', + '22050Hz_5s_brownnoise.wav', + '32000Hz_127f_sine440.wav', + ] + samplerates = [44100, 22050, 32000] + durations = [1, 5*22050, 127] + + for fname, samplerate, duration in zip(filenames, samplerates, durations): + output_name = os.path.join(output_dir, fname) + g = sink(output_name, samplerate) + total_frames = 0 + while total_frames < duration: + write = min(hop_size, duration - total_frames) + if 'brownnoise' in fname: + vec = np.random.rand(write).astype(float_type) * 2. - 1. + elif 'sine' in fname: + freq = 440 + t = np.arange(write).astype(float_type) + total_frames + vec = np.sin(2. * np.pi * freq * t / float(samplerate)) + else: + # silence + vec = fvec(write) + g(vec, write) + total_frames += write + outstr = "wrote {:2f}s".format(total_frames / float(samplerate)) + outstr += " ({:d} frames".format(total_frames) + outstr += " at {:d}Hz)".format(g.samplerate) + outstr += " to {:s}".format(g.uri) + print(outstr) diff --git a/python/demos/demo_filter.py b/python/demos/demo_filter.py new file mode 100755 index 00000000..10226cea --- /dev/null +++ b/python/demos/demo_filter.py @@ -0,0 +1,36 @@ +#! /usr/bin/env python + + +def apply_filter(path): + from aubio import source, sink, digital_filter + from os.path import basename, splitext + + # open input file, get its samplerate + s = source(path) + samplerate = s.samplerate + + # create an A-weighting filter + f = digital_filter(7) + f.set_a_weighting(samplerate) + # alternatively, apply another filter + + # create output file + o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate) + + total_frames = 0 + while True: + samples, read = s() + filtered_samples = f(samples) + o(filtered_samples, read) + total_frames += read + if read < s.hop_size: break + + 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)) + +if __name__ == '__main__': + import sys + for f in sys.argv[1:]: + apply_filter(f) diff --git a/python/demos/demo_filterbank.py b/python/demos/demo_filterbank.py index 1620a031..54aff573 100755 --- a/python/demos/demo_filterbank.py +++ b/python/demos/demo_filterbank.py @@ -1,7 +1,7 @@ #! /usr/bin/env python from aubio import filterbank, fvec -from pylab import loglog, show, subplot, xlim, ylim, xlabel, ylabel, title +from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title from numpy import vstack, arange win_s = 2048 @@ -19,7 +19,7 @@ coeffs[4] *= 5. f.set_coeffs(coeffs) -times = vstack([arange(win_s / 2 + 1) * samplerate / win_s] * n_filters) +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]) diff --git a/python/demos/demo_filterbank_slaney.py b/python/demos/demo_filterbank_slaney.py index 636dc809..f0e041e2 100755 --- a/python/demos/demo_filterbank_slaney.py +++ b/python/demos/demo_filterbank_slaney.py @@ -1,7 +1,7 @@ #! /usr/bin/env python from aubio import filterbank -from numpy import array, arange, vstack +from numpy import arange, vstack win_s = 8192 samplerate = 16000 @@ -11,7 +11,7 @@ f.set_mel_coeffs_slaney(samplerate) from pylab import loglog, title, show, xlim, ylim, xlabel, ylabel xlim([0,samplerate / 2]) -times = vstack([arange(win_s / 2 + 1) * samplerate / win_s] * 40) +times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * 40) loglog(times.T, f.get_coeffs().T, '.-') title('Mel frequency bands coefficients') xlim([100, 7500]) diff --git a/python/demos/demo_filterbank_triangle_bands.py b/python/demos/demo_filterbank_triangle_bands.py index 7b02e7d9..c65df93d 100755 --- a/python/demos/demo_filterbank_triangle_bands.py +++ b/python/demos/demo_filterbank_triangle_bands.py @@ -16,7 +16,7 @@ f.set_triangle_bands(freqs, samplerate) subplot(211) title('Examples of filterbank built with set_triangle_bands and set_coeffs') -times = vstack([arange(win_s / 2 + 1) * samplerate / win_s] * n_filters) +times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) loglog(times.T, f.get_coeffs().T, '.-') xlim([50, samplerate/2]) ylim([1.0e-6, 2.0e-2]) @@ -37,7 +37,7 @@ coeffs[4] *= 5. f.set_coeffs(coeffs) subplot(212) -times = vstack([arange(win_s / 2 + 1) * samplerate / win_s] * n_filters) +times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) loglog(times.T, f.get_coeffs().T, '.-') xlim([50, samplerate/2]) ylim([1.0e-6, 2.0e-2]) diff --git a/python/demos/demo_keyboard.py b/python/demos/demo_keyboard.py index 527d03bb..63786b9b 100755 --- a/python/demos/demo_keyboard.py +++ b/python/demos/demo_keyboard.py @@ -25,7 +25,6 @@ def get_keyboard_edges(firstnote = 21, lastnote = 108): return xb, xw, 2/3. *scaleb, 1/2. * scalew def create_keyboard_patches(firstnote, lastnote, ax = None): - import numpy as np import matplotlib.pyplot as plt from matplotlib.path import Path import matplotlib.patches as mpatches diff --git a/python/demos/demo_mel-energy.py b/python/demos/demo_mel-energy.py index ac098639..db27f7e2 100755 --- a/python/demos/demo_mel-energy.py +++ b/python/demos/demo_mel-energy.py @@ -1,14 +1,14 @@ #! /usr/bin/env python import sys -from aubio import fvec, source, pvoc, filterbank +from aubio import source, pvoc, filterbank from numpy import vstack, zeros win_s = 512 # fft size -hop_s = win_s / 4 # hop size +hop_s = win_s // 4 # hop size if len(sys.argv) < 2: - print "Usage: %s [samplerate]" % sys.argv[0] + print("Usage: %s [samplerate]" % sys.argv[0]) sys.exit(1) filename = sys.argv[1] @@ -34,14 +34,14 @@ while True: samples, read = s() fftgrain = pv(samples) new_energies = f(fftgrain) - print '%f' % (total_frames / float(samplerate) ), - print ' '.join(['%f' % b for b in new_energies]) + timestr = '%f' % (total_frames / float(samplerate) ) + print('{:s} {:s}'.format(timestr, ' '.join(['%f' % b for b in new_energies]))) energies = vstack( [energies, new_energies] ) total_frames += read if read < hop_s: break if 1: - print "done computing, now plotting" + print("done computing, now plotting") import matplotlib.pyplot as plt from demo_waveform_plot import get_waveform_plot from demo_waveform_plot import set_xlabels_sample2time diff --git a/python/demos/demo_mfcc.py b/python/demos/demo_mfcc.py index 2242f0cd..5a33d15f 100755 --- a/python/demos/demo_mfcc.py +++ b/python/demos/demo_mfcc.py @@ -2,20 +2,27 @@ import sys from aubio import source, pvoc, mfcc -from numpy import array, vstack, zeros +from numpy import vstack, zeros, diff -win_s = 512 # fft size -hop_s = win_s / 4 # hop size n_filters = 40 # must be 40 for mfcc n_coeffs = 13 -samplerate = 44100 if len(sys.argv) < 2: - print "Usage: %s " % sys.argv[0] + print("Usage: %s [samplerate] [win_s] [hop_s] [mode]" % sys.argv[0]) + print(" where [mode] can be 'delta' or 'ddelta' for first and second derivatives") sys.exit(1) source_filename = sys.argv[1] +if len(sys.argv) > 2: samplerate = int(sys.argv[2]) +else: samplerate = 0 +if len(sys.argv) > 3: win_s = int(sys.argv[3]) +else: win_s = 512 +if len(sys.argv) > 4: hop_s = int(sys.argv[4]) +else: hop_s = win_s // 4 +if len(sys.argv) > 5: mode = sys.argv[5] +else: mode = "default" + samplerate = 0 if len( sys.argv ) > 2: samplerate = int(sys.argv[2]) @@ -48,18 +55,28 @@ get_waveform_plot( source_filename, samplerate, block_size = hop_s, ax = wave) wave.xaxis.set_visible(False) wave.yaxis.set_visible(False) +# compute first and second derivatives +if mode in ["delta", "ddelta"]: + mfccs = diff(mfccs, axis = 0) +if mode == "ddelta": + mfccs = diff(mfccs, axis = 0) + all_times = arange(mfccs.shape[0]) * hop_s n_coeffs = mfccs.shape[1] for i in range(n_coeffs): ax = plt.axes ( [0.1, 0.75 - ((i+1) * 0.65 / n_coeffs), 0.8, 0.65 / n_coeffs], sharex = wave ) ax.xaxis.set_visible(False) - ax.yaxis.set_visible(False) + ax.set_yticks([]) + ax.set_ylabel('%d' % i) ax.plot(all_times, mfccs.T[i]) # add time to the last axis -set_xlabels_sample2time( ax, frames_read, samplerate) +set_xlabels_sample2time( ax, frames_read, samplerate) #plt.ylabel('spectral descriptor value') ax.xaxis.set_visible(True) -wave.set_title('MFCC for %s' % source_filename) +title = 'MFCC for %s' % source_filename +if mode == "delta": title = mode + " " + title +elif mode == "ddelta": title = "double-delta" + " " + title +wave.set_title(title) plt.show() diff --git a/python/demos/demo_notes.py b/python/demos/demo_notes.py new file mode 100755 index 00000000..301013a6 --- /dev/null +++ b/python/demos/demo_notes.py @@ -0,0 +1,37 @@ +#! /usr/bin/env python + +import sys +from aubio import source, notes + +if len(sys.argv) < 2: + print("Usage: %s [samplerate]" % sys.argv[0]) + sys.exit(1) + +filename = sys.argv[1] + +downsample = 1 +samplerate = 44100 // downsample +if len( sys.argv ) > 2: samplerate = int(sys.argv[2]) + +win_s = 512 // downsample # fft size +hop_s = 256 // downsample # hop size + +s = source(filename, samplerate, hop_s) +samplerate = s.samplerate + +tolerance = 0.8 + +notes_o = notes("default", win_s, hop_s, samplerate) + +print("%8s" % "time","[ start","vel","last ]") + +# total number of frames read +total_frames = 0 +while True: + samples, read = s() + new_note = notes_o(samples) + if (new_note[0] != 0): + note_str = ' '.join(["%.2f" % i for i in new_note]) + print("%.6f" % (total_frames/float(samplerate)), new_note) + total_frames += read + if read < hop_s: break diff --git a/python/demos/demo_onset.py b/python/demos/demo_onset.py index 949a6639..43e4aede 100755 --- a/python/demos/demo_onset.py +++ b/python/demos/demo_onset.py @@ -4,10 +4,10 @@ import sys from aubio import source, onset win_s = 512 # fft size -hop_s = win_s / 2 # hop size +hop_s = win_s // 2 # hop size if len(sys.argv) < 2: - print "Usage: %s [samplerate]" % sys.argv[0] + print("Usage: %s [samplerate]" % sys.argv[0]) sys.exit(1) filename = sys.argv[1] @@ -28,7 +28,7 @@ total_frames = 0 while True: samples, read = s() if o(samples): - print "%f" % o.get_last_s() + print("%f" % o.get_last_s()) onsets.append(o.get_last()) total_frames += read if read < hop_s: break diff --git a/python/demos/demo_onset_plot.py b/python/demos/demo_onset_plot.py index a9286558..5169cf7c 100755 --- a/python/demos/demo_onset_plot.py +++ b/python/demos/demo_onset_plot.py @@ -2,13 +2,13 @@ import sys from aubio import onset, source -from numpy import array, hstack, zeros +from numpy import hstack, zeros win_s = 512 # fft size -hop_s = win_s / 2 # hop size +hop_s = win_s // 2 # hop size if len(sys.argv) < 2: - print "Usage: %s [samplerate]" % sys.argv[0] + print("Usage: %s [samplerate]" % sys.argv[0]) sys.exit(1) filename = sys.argv[1] @@ -34,10 +34,10 @@ total_frames = 0 while True: samples, read = s() if o(samples): - print "%f" % (o.get_last_s()) + print("%f" % (o.get_last_s())) onsets.append(o.get_last()) # keep some data to plot it later - new_maxes = (abs(samples.reshape(hop_s/downsample, downsample))).max(axis=0) + new_maxes = (abs(samples.reshape(hop_s//downsample, downsample))).max(axis=0) allsamples_max = hstack([allsamples_max, new_maxes]) desc.append(o.get_descriptor()) tdesc.append(o.get_thresholded_descriptor()) @@ -46,7 +46,6 @@ while True: if 1: # do plotting - from numpy import arange import matplotlib.pyplot as plt allsamples_max = (allsamples_max > 0) * allsamples_max allsamples_max_times = [ float(t) * hop_s / downsample / samplerate for t in range(len(allsamples_max)) ] @@ -62,9 +61,10 @@ if 1: plt1.xaxis.set_visible(False) plt1.yaxis.set_visible(False) desc_times = [ float(t) * hop_s / samplerate for t in range(len(desc)) ] - desc_plot = [d / max(desc) for d in desc] + desc_max = max(desc) if max(desc) != 0 else 1. + desc_plot = [d / desc_max for d in desc] plt2.plot(desc_times, desc_plot, '-g') - tdesc_plot = [d / max(desc) for d in tdesc] + tdesc_plot = [d / desc_max for d in tdesc] for stamp in onsets: stamp /= float(samplerate) plt2.plot([stamp, stamp], [min(tdesc_plot), max(desc_plot)], '-r') diff --git a/python/demos/demo_pitch.py b/python/demos/demo_pitch.py index 2eb9ba7b..555a30e6 100755 --- a/python/demos/demo_pitch.py +++ b/python/demos/demo_pitch.py @@ -1,20 +1,20 @@ #! /usr/bin/env python import sys -from aubio import source, pitch, freqtomidi +from aubio import source, pitch if len(sys.argv) < 2: - print "Usage: %s [samplerate]" % sys.argv[0] + print("Usage: %s [samplerate]" % sys.argv[0]) sys.exit(1) filename = sys.argv[1] downsample = 1 -samplerate = 44100 / downsample +samplerate = 44100 // downsample if len( sys.argv ) > 2: samplerate = int(sys.argv[2]) -win_s = 4096 / downsample # fft size -hop_s = 512 / downsample # hop size +win_s = 4096 // downsample # fft size +hop_s = 512 // downsample # hop size s = source(filename, samplerate, hop_s) samplerate = s.samplerate @@ -36,7 +36,7 @@ while True: #pitch = int(round(pitch)) confidence = pitch_o.get_confidence() #if confidence < 0.8: pitch = 0. - #print "%f %f %f" % (total_frames / float(samplerate), pitch, confidence) + print("%f %f %f" % (total_frames / float(samplerate), pitch, confidence)) pitches += [pitch] confidences += [confidence] total_frames += read @@ -45,6 +45,7 @@ while True: if 0: sys.exit(0) #print pitches +import os.path from numpy import array, ma import matplotlib.pyplot as plt from demo_waveform_plot import get_waveform_plot, set_xlabels_sample2time @@ -63,14 +64,11 @@ plt.setp(ax1.get_xticklabels(), visible = False) ax1.set_xlabel('') def array_from_text_file(filename, dtype = 'float'): - import os.path - from numpy import array filename = os.path.join(os.path.dirname(__file__), filename) return array([line.split() for line in open(filename).readlines()], dtype = dtype) ax2 = fig.add_subplot(312, sharex = ax1) -import sys, os.path ground_truth = os.path.splitext(filename)[0] + '.f0.Corrected' if os.path.isfile(ground_truth): ground_truth = array_from_text_file(ground_truth) diff --git a/python/demos/demo_pitch_sinusoid.py b/python/demos/demo_pitch_sinusoid.py index 6407e586..629f3273 100755 --- a/python/demos/demo_pitch_sinusoid.py +++ b/python/demos/demo_pitch_sinusoid.py @@ -1,20 +1,17 @@ #! /usr/bin/env python -from numpy import random, sin, arange, ones, zeros -from math import pi -from aubio import fvec, pitch +import numpy as np +import aubio def build_sinusoid(length, freqs, samplerate): - return sin( 2. * pi * arange(length) * freqs / samplerate) + return np.sin( 2. * np.pi * np.arange(length) * freqs / samplerate).astype(aubio.float_type) def run_pitch(p, input_vec): - f = fvec (p.hop_size) - cands = [] - count = 0 - for vec_slice in input_vec.reshape((-1, p.hop_size)): - f[:] = vec_slice - cands.append(p(f)) - return cands + cands = [] + for vec_slice in input_vec.reshape((-1, p.hop_size)): + a = p(vec_slice)[0] + cands.append(a) + return cands methods = ['default', 'schmitt', 'fcomb', 'mcomb', 'yin', 'yinfft'] @@ -23,9 +20,9 @@ buf_size = 2048 hop_size = 512 samplerate = 44100 sin_length = (samplerate * 10) % 512 * 512 -freqs = zeros(sin_length) +freqs = np.zeros(sin_length) -partition = sin_length / 8 +partition = sin_length // 8 pointer = 0 pointer += partition @@ -40,29 +37,35 @@ freqs[ pointer : pointer + partition ] = 1480 pointer += partition pointer += partition -freqs[ pointer : pointer + partition ] = 400 + 5 * random.random(sin_length/8) +freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length/8) a = build_sinusoid(sin_length, freqs, samplerate) for method in methods: - p = pitch(method, buf_size, hop_size, samplerate) - cands[method] = run_pitch(p, a) + p = aubio.pitch(method, buf_size, hop_size, samplerate) + cands[method] = run_pitch(p, a) + print(method) + print(cands[method]) -print "done computing" +print("done computing") if 1: - from pylab import plot, show, xlabel, ylabel, legend, ylim - ramp = arange(0, sin_length / hop_size).astype('float') * hop_size / samplerate - for method in methods: - plot(ramp, cands[method],'.-') + import matplotlib.pyplot as plt - # plot ground truth - ramp = arange(0, sin_length).astype('float') / samplerate - plot(ramp, freqs, ':') + # times + ramp = np.arange(0, sin_length / hop_size).astype('float') * hop_size / samplerate - legend(methods+['ground truth'], 'upper right') - xlabel('time (s)') - ylabel('frequency (Hz)') - ylim([0,2000]) - show() + # plot each result + for method in methods: + plt.plot(ramp, cands[method], '.-', label=method) + # plot ground truth + ramp = np.arange(0, sin_length).astype('float') / samplerate + plt.plot(ramp, freqs, ':', label = 'ground truth') + + plt.legend(loc='upper left') + + plt.xlabel('time (s)') + plt.ylabel('frequency (Hz)') + plt.ylim([0,2000]) + plt.show() diff --git a/python/demos/demo_pyaudio.py b/python/demos/demo_pyaudio.py new file mode 100755 index 00000000..4c14cd54 --- /dev/null +++ b/python/demos/demo_pyaudio.py @@ -0,0 +1,75 @@ +#! /usr/bin/env python + +# Use pyaudio to open the microphone and run aubio.pitch on the stream of +# incoming samples. If a filename is given as the first argument, it will +# record 5 seconds of audio to this location. Otherwise, the script will +# run until Ctrl+C is pressed. + +# Examples: +# $ ./python/demos/demo_pyaudio.py +# $ ./python/demos/demo_pyaudio.py /tmp/recording.wav + +import pyaudio +import sys +import numpy as np +import aubio + +# initialise pyaudio +p = pyaudio.PyAudio() + +# open stream +buffer_size = 1024 +pyaudio_format = pyaudio.paFloat32 +n_channels = 1 +samplerate = 44100 +stream = p.open(format=pyaudio_format, + channels=n_channels, + rate=samplerate, + input=True, + frames_per_buffer=buffer_size) + +if len(sys.argv) > 1: + # record 5 seconds + output_filename = sys.argv[1] + record_duration = 5 # exit 1 + outputsink = aubio.sink(sys.argv[1], samplerate) + total_frames = 0 +else: + # run forever + outputsink = None + record_duration = None + +# setup pitch +tolerance = 0.8 +win_s = 4096 # fft size +hop_s = buffer_size # hop size +pitch_o = aubio.pitch("default", win_s, hop_s, samplerate) +pitch_o.set_unit("midi") +pitch_o.set_tolerance(tolerance) + +print("*** starting recording") +while True: + try: + audiobuffer = stream.read(buffer_size) + signal = np.fromstring(audiobuffer, dtype=np.float32) + + pitch = pitch_o(signal)[0] + confidence = pitch_o.get_confidence() + + print("{} / {}".format(pitch,confidence)) + + if outputsink: + outputsink(signal, len(signal)) + + if record_duration: + total_frames += len(signal) + if record_duration * samplerate < total_frames: + break + except KeyboardInterrupt: + print("*** Ctrl+C pressed, exiting") + break + +print("*** done recording") +stream.stop_stream() +stream.close() +p.terminate() diff --git a/python/demos/demo_pysoundcard_record.py b/python/demos/demo_pysoundcard_record.py index e3816d2a..b97a838d 100755 --- a/python/demos/demo_pysoundcard_record.py +++ b/python/demos/demo_pysoundcard_record.py @@ -10,7 +10,6 @@ def record_sink(sink_path): duration = 5 # in seconds s = Stream(blocksize = hop_size, channels = 1) g = sink(sink_path, samplerate = int(s.samplerate)) - print s.channels s.start() total_frames = 0 @@ -21,9 +20,9 @@ def record_sink(sink_path): mono_vec = vec.sum(-1) / float(s.channels[0]) g(mono_vec, hop_size) total_frames += hop_size - except KeyboardInterrupt, e: - print "stopped after", "%.2f seconds" % (total_frames / s.samplerate) - pass + except KeyboardInterrupt: + duration = total_frames / float(s.samplerate) + print("stopped after %.2f seconds" % duration) s.stop() if __name__ == '__main__': diff --git a/python/demos/demo_reading_speed.py b/python/demos/demo_reading_speed.py new file mode 100755 index 00000000..90739f3f --- /dev/null +++ b/python/demos/demo_reading_speed.py @@ -0,0 +1,139 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +""" + +Compare the speed of several methods for reading and loading a sound file. + +Optionally, this file can make use of the following packages: + + - audioread https://github.com/beetbox/audioread + - scipy https://scipy.org + - librosa https://github.com/bmcfee/librosa + - pydub https://github.com/jiaaro/pydub + +Uncomment the function names below and send us your speed results! + +""" + + +test_functions = [ + "read_file_aubio", + "load_file_aubio", + #"load_file_scipy", + #"load_file_scipy_mmap", + #"read_file_audioread", + #"load_file_librosa", + #"read_file_pydub", + #"load_file_pydub", + ] + + +import numpy as np + +def read_file_audioread(filename): + import audioread + # taken from librosa.util.utils + def convert_buffer_to_float(buf, n_bytes = 2, dtype = np.float32): + # Invert the scale of the data + scale = 1./float(1 << ((8 * n_bytes) - 1)) + # Construct the format string + fmt = ' ' % sys.argv[0] - sys.exit(1) - samplerate = 44100 - f = source(sys.argv[1], samplerate, 256) - g = sink(sys.argv[2], samplerate) - total_frames, read = 0, 256 + if len(sys.argv) < 2: + print('usage: %s ' % sys.argv[0]) + sys.exit(1) + samplerate = 44100 + f = source(sys.argv[1], samplerate, 256) + g = sink(sys.argv[2], samplerate) + total_frames, read = 0, 256 - win_s = 512 # fft size - hop_s = win_s / 2 # hop size - pv = pvoc(win_s, hop_s) # phase vocoder + win_s = 512 # fft size + hop_s = win_s // 2 # hop size + pv = pvoc(win_s, hop_s) # phase vocoder - while read: - samples, read = f() - spectrum = pv(samples) # compute spectrum - #spectrum.norm *= .8 # reduce amplitude a bit - spectrum.phas[:] = 0. # zero phase - new_samples = pv.rdo(spectrum) # compute modified samples - g(new_samples, read) # write to output - total_frames += read + while read: + samples, read = f() + spectrum = pv(samples) # compute spectrum + #spectrum.norm *= .8 # reduce amplitude a bit + spectrum.phas[:] = 0. # zero phase + new_samples = pv.rdo(spectrum) # compute modified samples + g(new_samples, read) # write to output + total_frames += read - print "wrote", total_frames, "from", f.uri, "to", g.uri - - + format_str = "read {:d} samples from {:s}, written to {:s}" + print(format_str.format(total_frames, f.uri, g.uri)) diff --git a/python/demos/demo_simple_spectral_weighting.py b/python/demos/demo_simple_spectral_weighting.py index b84f7d8d..dd4abbe4 100755 --- a/python/demos/demo_simple_spectral_weighting.py +++ b/python/demos/demo_simple_spectral_weighting.py @@ -13,7 +13,7 @@ def hanningz(size): if __name__ == '__main__': if len(sys.argv) < 2: - print 'usage: %s ' % sys.argv[0] + print('usage: %s ' % sys.argv[0]) sys.exit(1) samplerate = 0 if len(sys.argv) > 3: samplerate = int(sys.argv[3]) @@ -22,7 +22,7 @@ if __name__ == '__main__': g = sink(sys.argv[2], samplerate) win_s = 512 # fft size - hop_s = win_s / 2 # hop size + hop_s = win_s // 2 # hop size pv = pvoc(win_s, hop_s) # phase vocoder # spectral weighting vector @@ -30,7 +30,7 @@ if __name__ == '__main__': .8 * hanningz(80)[40:], zeros( 50 ), 1.3 * hanningz(100), - zeros (win_s / 2 + 1 - 40 - 50 - 100), + zeros (win_s // 2 + 1 - 40 - 50 - 100), ] ) if 0: @@ -52,4 +52,5 @@ if __name__ == '__main__': g(new_samples, read) total_frames += read - print "read", total_frames / float(samplerate), "seconds from", f.uri + duration = total_frames / float(samplerate) + print("read {:.3f}s from {:s}".format(duration, f.uri)) diff --git a/python/demos/demo_sink.py b/python/demos/demo_sink.py index 93139ae8..ce2bb1b6 100755 --- a/python/demos/demo_sink.py +++ b/python/demos/demo_sink.py @@ -5,7 +5,7 @@ from aubio import source, sink if __name__ == '__main__': if len(sys.argv) < 3: - print 'usage: %s [samplerate] [hop_size]' % sys.argv[0] + print('usage: %s [samplerate] [hop_size]' % sys.argv[0]) sys.exit(1) if len(sys.argv) > 3: samplerate = int(sys.argv[3]) @@ -22,8 +22,10 @@ if __name__ == '__main__': vec, read = f() g(vec, read) total_frames += read - print "wrote", "%.2fs" % (total_frames / float(samplerate) ), - print "(", total_frames, "frames", "in", - print total_frames / f.hop_size, "blocks", "at", "%dHz" % f.samplerate, ")", - print "from", f.uri, - print "to", g.uri + outstr = "wrote %.2fs" % (total_frames / float(samplerate)) + outstr += " (%d frames in" % total_frames + outstr += " %d blocks" % (total_frames // f.hop_size) + outstr += " at %dHz)" % f.samplerate + outstr += " from " + f.uri + outstr += " to " + g.uri + print(outstr) diff --git a/python/demos/demo_sink_create_woodblock.py b/python/demos/demo_sink_create_woodblock.py index 2c06a5fa..5a42adcb 100755 --- a/python/demos/demo_sink_create_woodblock.py +++ b/python/demos/demo_sink_create_woodblock.py @@ -2,11 +2,11 @@ import sys from math import pi, e -from aubio import sink -from numpy import arange, resize, sin, exp, zeros +from aubio import sink, float_type +from numpy import arange, sin, exp, zeros if len(sys.argv) < 2: - print 'usage: %s [samplerate]' % sys.argv[0] + print('usage: %s [samplerate]' % sys.argv[0]) sys.exit(1) samplerate = 44100 # samplerate in Hz @@ -25,9 +25,9 @@ decay = .5 period = float(samplerate) / pitch # create a sine lookup table tablelen = 1000 -sinetable = arange(tablelen + 1, dtype = 'float32') +sinetable = arange(tablelen + 1, dtype = float_type) sinetable = 0.7 * sin(twopi * sinetable/tablelen) -sinetone = zeros((duration,), dtype = 'float32') +sinetone = zeros((duration,), dtype = float_type) # compute sinetone at floating point period for i in range(duration): @@ -39,7 +39,7 @@ for i in range(duration): sinetone[i] = a + frac * (b -a) # apply some envelope -float_ramp = arange(duration, dtype = 'float32') +float_ramp = arange(duration, dtype = float_type) sinetone *= exp( - e * float_ramp / duration / decay) sinetone[:attack] *= exp( e * ( float_ramp[:attack] / attack - 1 ) ) diff --git a/python/demos/demo_sink_multi.py b/python/demos/demo_sink_multi.py index dcf20da7..70eb6360 100755 --- a/python/demos/demo_sink_multi.py +++ b/python/demos/demo_sink_multi.py @@ -5,7 +5,7 @@ from aubio import source, sink if __name__ == '__main__': if len(sys.argv) < 3: - print 'usage: %s [samplerate] [hop_size]' % sys.argv[0] + print('usage: %s [samplerate] [hop_size]' % sys.argv[0]) sys.exit(1) if len(sys.argv) > 3: samplerate = int(sys.argv[3]) @@ -22,10 +22,11 @@ if __name__ == '__main__': vec, read = f.do_multi() g.do_multi(vec, read) total_frames += read - print "wrote", "%.2fs" % (total_frames / float(samplerate) ), - print "(", total_frames, "frames", "in", - print total_frames / f.hop_size, "blocks", - print "of", f.channels, "channels", - print "at", "%dHz" % f.samplerate, ")", - print "from", f.uri, - print "to", g.uri + outstr = "wrote %.2fs" % (total_frames / float(samplerate)) + outstr += " (%d frames in" % total_frames + outstr += " %d blocks" % (total_frames // f.hop_size) + outstr += " of %d channels" % f.channels + outstr += " at %dHz)" % f.samplerate + outstr += " from " + f.uri + outstr += " to " + g.uri + print(outstr) diff --git a/python/demos/demo_slicing.py b/python/demos/demo_slicing.py index a21252ad..d10d0af6 100755 --- a/python/demos/demo_slicing.py +++ b/python/demos/demo_slicing.py @@ -6,7 +6,7 @@ from aubio import source, sink if __name__ == '__main__': if len(sys.argv) < 3: - print 'usage: %s ' % sys.argv[0] + print('usage: %s ' % sys.argv[0]) sys.exit(1) source_file = sys.argv[1] duration = float(sys.argv[2]) @@ -44,7 +44,8 @@ if __name__ == '__main__': total_frames_written += read total_duration = total_frames_written / float(samplerate) slice_n += 1 - print 'created %(slice_n)s slices from %(source_base_name)s%(source_ext)s' % locals(), - print ' (total duration %(total_duration).2fs)' % locals() + outstr = 'created %(slice_n)s slices from %(source_base_name)s%(source_ext)s' % locals() + outstr += ' (total duration %(total_duration).2fs)' % locals() + print(outstr) # close source and sink files del f, g diff --git a/python/demos/demo_source.py b/python/demos/demo_source.py index 30318b1d..c60d785f 100755 --- a/python/demos/demo_source.py +++ b/python/demos/demo_source.py @@ -5,7 +5,7 @@ from aubio import source if __name__ == '__main__': if len(sys.argv) < 2: - print 'usage: %s [samplerate] [hop_size]' % sys.argv[0] + print('usage: %s [samplerate] [hop_size]' % sys.argv[0]) sys.exit(1) samplerate = 0 hop_size = 256 @@ -20,7 +20,9 @@ if __name__ == '__main__': vec, read = f() total_frames += read if read < f.hop_size: break - print "read", "%.2fs" % (total_frames / float(samplerate) ), - print "(", total_frames, "frames", "in", - print total_frames / f.hop_size, "blocks", "at", "%dHz" % f.samplerate, ")", - print "from", f.uri + outstr = "read %.2fs" % (total_frames / float(samplerate)) + outstr += " (%d frames in" % total_frames + outstr += " %d blocks" % (total_frames // f.hop_size) + outstr += " at %dHz)" % f.samplerate + outstr += " from " + f.uri + print(outstr) diff --git a/python/demos/demo_source_simple.py b/python/demos/demo_source_simple.py new file mode 100755 index 00000000..46bd8bee --- /dev/null +++ b/python/demos/demo_source_simple.py @@ -0,0 +1,16 @@ +#! /usr/bin/env python +import sys, 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) +total_frames = 0 + +while True: # reading loop + samples, read = s() + total_frames += read + if read < hop_size: break # end of file reached + +fmt_string = "read {:d} frames at {:d}Hz from {:s}" +print (fmt_string.format(total_frames, s.samplerate, sys.argv[1])) + diff --git a/python/demos/demo_specdesc.py b/python/demos/demo_specdesc.py index b1520293..6afc7f48 100755 --- a/python/demos/demo_specdesc.py +++ b/python/demos/demo_specdesc.py @@ -1,14 +1,14 @@ #! /usr/bin/env python import sys -from aubio import fvec, source, pvoc, specdesc -from numpy import hstack +import numpy as np +from aubio import source, pvoc, specdesc win_s = 512 # fft size -hop_s = win_s / 4 # hop size +hop_s = win_s // 4 # hop size if len(sys.argv) < 2: - print "Usage: %s [samplerate]" % sys.argv[0] + print("Usage: %s [samplerate]" % sys.argv[0]) sys.exit(1) filename = sys.argv[1] @@ -30,7 +30,7 @@ o = {} for method in methods: cands = [] - all_descs[method] = fvec(0) + all_descs[method] = np.array([]) o[method] = specdesc(method, win_s) total_frames = 0 @@ -39,17 +39,17 @@ downsample = 2 while True: samples, read = s() fftgrain = pv(samples) - #print "%f" % ( total_frames / float(samplerate) ), + #outstr = "%f" % ( total_frames / float(samplerate) ) for method in methods: specdesc_val = o[method](fftgrain)[0] - all_descs[method] = hstack ( [all_descs[method], specdesc_val] ) - #print "%f" % specdesc_val, - #print + all_descs[method] = np.append(all_descs[method], specdesc_val) + #outstr += " %f" % specdesc_val + #print(outstr) total_frames += read if read < hop_s: break if 1: - print "done computing, now plotting" + print("done computing, now plotting") import matplotlib.pyplot as plt from demo_waveform_plot import get_waveform_plot from demo_waveform_plot import set_xlabels_sample2time diff --git a/python/demos/demo_spectrogram.py b/python/demos/demo_spectrogram.py index cf5f80d5..51be9174 100755 --- a/python/demos/demo_spectrogram.py +++ b/python/demos/demo_spectrogram.py @@ -1,63 +1,77 @@ #! /usr/bin/env python -import sys -from aubio import pvoc, source -from numpy import array, arange, zeros, shape, log10, vstack -from pylab import imshow, show, cm, axis, ylabel, xlabel, xticks, yticks +import sys, os.path +from aubio import pvoc, source, float_type +from numpy import zeros, log10, vstack +import matplotlib.pyplot as plt def get_spectrogram(filename, samplerate = 0): - win_s = 512 # fft window size - hop_s = win_s / 2 # hop size - fft_s = win_s / 2 + 1 # spectrum bins + win_s = 512 # fft window size + hop_s = win_s // 2 # hop size + fft_s = win_s // 2 + 1 # spectrum bins - a = source(filename, samplerate, hop_s) # source file - if samplerate == 0: samplerate = a.samplerate - pv = pvoc(win_s, hop_s) # phase vocoder - specgram = zeros([0, fft_s], dtype='float32') # numpy array to store spectrogram + a = source(filename, samplerate, hop_s) # source file + if samplerate == 0: samplerate = a.samplerate + pv = pvoc(win_s, hop_s) # phase vocoder + specgram = zeros([0, fft_s], dtype=float_type) # numpy array to store spectrogram - # analysis - while True: - samples, read = a() # read file - specgram = vstack((specgram,pv(samples).norm)) # store new norm vector - if read < a.hop_size: break + # analysis + while True: + samples, read = a() # read file + specgram = vstack((specgram,pv(samples).norm)) # store new norm vector + if read < a.hop_size: break - # plotting - imshow(log10(specgram.T + .001), origin = 'bottom', aspect = 'auto', cmap=cm.gray_r) - axis([0, len(specgram), 0, len(specgram[0])]) - # show axes in Hz and seconds - time_step = hop_s / float(samplerate) - total_time = len(specgram) * time_step - print "total time: %0.2fs" % total_time, - print ", samplerate: %.2fkHz" % (samplerate / 1000.) - n_xticks = 10 - n_yticks = 10 + # plotting + fig = plt.imshow(log10(specgram.T + .001), origin = 'bottom', aspect = 'auto', cmap=plt.cm.gray_r) + ax = fig.axes + ax.axis([0, len(specgram), 0, len(specgram[0])]) + # show axes in Hz and seconds + time_step = hop_s / float(samplerate) + total_time = len(specgram) * time_step + outstr = "total time: %0.2fs" % total_time + print(outstr + ", samplerate: %.2fkHz" % (samplerate / 1000.)) + n_xticks = 10 + n_yticks = 10 - def get_rounded_ticks( top_pos, step, n_ticks ): - top_label = top_pos * step - # get the first label - ticks_first_label = top_pos * step / n_ticks - # round to the closest .1 - ticks_first_label = round ( ticks_first_label * 10. ) / 10. - # compute all labels from the first rounded one - ticks_labels = [ ticks_first_label * n for n in range(n_ticks) ] + [ top_label ] - # get the corresponding positions - ticks_positions = [ ticks_labels[n] / step for n in range(n_ticks) ] + [ top_pos ] - # convert to string - ticks_labels = [ "%.1f" % x for x in ticks_labels ] - # return position, label tuple to use with x/yticks - return ticks_positions, ticks_labels - - # apply to the axis - xticks( *get_rounded_ticks ( len(specgram), time_step, n_xticks ) ) - yticks( *get_rounded_ticks ( len(specgram[0]), (samplerate / 2. / 1000.) / len(specgram[0]), n_yticks ) ) - ylabel('Frequency (kHz)') - xlabel('Time (s)') + def get_rounded_ticks( top_pos, step, n_ticks ): + top_label = top_pos * step + # get the first label + ticks_first_label = top_pos * step / n_ticks + # round to the closest .1 + ticks_first_label = round ( ticks_first_label * 10. ) / 10. + # compute all labels from the first rounded one + ticks_labels = [ ticks_first_label * n for n in range(n_ticks) ] + [ top_label ] + # get the corresponding positions + ticks_positions = [ ticks_labels[n] / step for n in range(n_ticks) ] + [ top_pos ] + # convert to string + ticks_labels = [ "%.1f" % x for x in ticks_labels ] + # return position, label tuple to use with x/yticks + return ticks_positions, ticks_labels + + # apply to the axis + x_ticks, x_labels = get_rounded_ticks ( len(specgram), time_step, n_xticks ) + y_ticks, y_labels = get_rounded_ticks ( len(specgram[0]), (samplerate / 1000. / 2.) / len(specgram[0]), n_yticks ) + ax.set_xticks( x_ticks ) + ax.set_yticks ( y_ticks ) + ax.set_xticklabels( x_labels ) + ax.set_yticklabels ( y_labels ) + ax.set_ylabel('Frequency (kHz)') + ax.set_xlabel('Time (s)') + ax.set_title(os.path.basename(filename)) + for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] + + ax.get_xticklabels() + ax.get_yticklabels()): + item.set_fontsize('x-small') + return fig if __name__ == '__main__': - if len(sys.argv) < 2: - print "Usage: %s " % sys.argv[0] - else: - for soundfile in sys.argv[1:]: - get_spectrogram(soundfile) - # display graph - show() + if len(sys.argv) < 2: + print("Usage: %s " % sys.argv[0]) + else: + for soundfile in sys.argv[1:]: + fig = get_spectrogram(soundfile) + # display graph + plt.show() + #outimage = os.path.basename(soundfile) + '.png' + #print ("writing: " + outimage) + #plt.savefig(outimage) + plt.close() diff --git a/python/demos/demo_tempo.py b/python/demos/demo_tempo.py index 17b959be..51e1ae32 100755 --- a/python/demos/demo_tempo.py +++ b/python/demos/demo_tempo.py @@ -4,10 +4,10 @@ import sys from aubio import tempo, source win_s = 512 # fft size -hop_s = win_s / 2 # hop size +hop_s = win_s // 2 # hop size if len(sys.argv) < 2: - print "Usage: %s [samplerate]" % sys.argv[0] + print("Usage: %s [samplerate]" % sys.argv[0]) sys.exit(1) filename = sys.argv[1] @@ -33,7 +33,7 @@ while True: is_beat = o(samples) if is_beat: this_beat = int(total_frames - delay + is_beat[0] * hop_s) - print "%f" % (this_beat / float(samplerate)) + print("%f" % (this_beat / float(samplerate))) beats.append(this_beat) total_frames += read if read < hop_s: break diff --git a/python/demos/demo_tempo_plot.py b/python/demos/demo_tempo_plot.py index de06d001..48a6ccf3 100755 --- a/python/demos/demo_tempo_plot.py +++ b/python/demos/demo_tempo_plot.py @@ -4,10 +4,10 @@ import sys from aubio import tempo, source win_s = 512 # fft size -hop_s = win_s / 2 # hop size +hop_s = win_s // 2 # hop size if len(sys.argv) < 2: - print "Usage: %s [samplerate]" % sys.argv[0] + print("Usage: %s [samplerate]" % sys.argv[0]) sys.exit(1) filename = sys.argv[1] @@ -39,11 +39,11 @@ while True: if len(beats) > 1: # do plotting - from numpy import array, arange, mean, median, diff + from numpy import mean, median, diff import matplotlib.pyplot as plt bpms = 60./ diff(beats) - print 'mean period:', "%.2f" % mean(bpms), 'bpm', 'median', "%.2f" % median(bpms), 'bpm' - print 'plotting', filename + print('mean period: %.2fbpm, median: %.2fbpm' % (mean(bpms), median(bpms))) + print('plotting %s' % filename) plt1 = plt.axes([0.1, 0.75, 0.8, 0.19]) plt2 = plt.axes([0.1, 0.1, 0.8, 0.65], sharex = plt1) plt.rc('lines',linewidth='.8') @@ -75,5 +75,5 @@ if len(beats) > 1: plt.show() else: - print 'mean period:', "%.2f" % 0, 'bpm', 'median', "%.2f" % 0, 'bpm', - print 'nothing to plot, file too short?' + print('mean period: %.2fbpm, median: %.2fbpm' % (0, 0)) + print('plotting %s' % filename) diff --git a/python/demos/demo_timestretch.py b/python/demos/demo_timestretch.py new file mode 100755 index 00000000..2271d01a --- /dev/null +++ b/python/demos/demo_timestretch.py @@ -0,0 +1,110 @@ +#! /usr/bin/env python + +# Implementation of the timescale algorithm according to Dan Ellis, *A Phase +# Vocoder in Matlab*. http://www.ee.columbia.edu/~dpwe/resources/matlab/pvoc/ + +# This file follows the original implementation, with analysis in a first pass, +# and synthesis in a second pass. + +import sys +from aubio import source, sink, pvoc, cvec +from aubio import unwrap2pi, float_type +import numpy as np + +win_s = 1024 +hop_s = win_s // 8 # 87.5 % overlap + +warmup = win_s // hop_s - 1 + +if len(sys.argv) < 3: + print("Usage: {:s} [samplerate]".format(sys.argv[0])) + print("""Examples: + # twice faster + {0} track_01.mp3 track_01_faster.wav 2.0 + # twice slower + {0} track_02.flac track_02_slower.wav 0.5 + # one and a half time faster, resampling first the input to 22050 + {0} track_02.flac track_02_slower.wav 1.5 22050""".format(sys.argv[0])) + sys.exit(1) + +source_filename = sys.argv[1] +output_filename = sys.argv[2] +rate = float(sys.argv[3]) + +samplerate = 0 if len(sys.argv) < 5 else int(sys.argv[4]) +source_in = source(source_filename, samplerate, hop_s) +samplerate = source_in.samplerate +p = pvoc(win_s, hop_s) + +# allocate memory to store norms and phases +n_blocks = source_in.duration // hop_s + 1 +# adding an empty frame at end of spectrogram +norms = np.zeros((n_blocks + 1, win_s // 2 + 1), dtype = float_type) +phases = np.zeros((n_blocks + 1, win_s // 2 + 1), dtype = float_type) + +block_read = 0 +while True: + # read from source + samples, read = source_in() + # compute fftgrain + spec = p(samples) + # store current grain + norms[block_read] = spec.norm + phases[block_read] = spec.phas + # until end of file + if read < hop_s: break + # increment block counter + block_read += 1 + +# just to make sure +#source_in.close() + +sink_out = sink(output_filename, samplerate) + +# interpolated time steps (j = alpha * i) +steps = np.arange(0, n_blocks, rate, dtype = float_type) +# initial phase +phas_acc = phases[0] +# excepted phase advance in each bin +phi_advance = np.linspace(0, np.pi * hop_s, win_s / 2 + 1).astype (float_type) + +new_grain = cvec(win_s) + +for (t, step) in enumerate(steps): + + frac = 1. - np.mod(step, 1.0) + # get pair of frames + t_norms = norms[int(step):int(step+2)] + t_phases = phases[int(step):int(step+2)] + + # compute interpolated frame + new_grain.norm = frac * t_norms[0] + (1. - frac) * t_norms[1] + new_grain.phas = phas_acc + #print t, step, new_grain.norm + #print t, step, phas_acc + + # psola + samples = p.rdo(new_grain) + if t > warmup: # skip the first few frames to warm up phase vocoder + # write to sink + sink_out(samples, hop_s) + + # calculate phase advance + dphas = t_phases[1] - t_phases[0] - phi_advance + # unwrap angle to [-pi; pi] + dphas = unwrap2pi(dphas) + # cumulate phase, to be used for next frame + phas_acc += phi_advance + dphas + +for t in range(warmup + 1): # purge the last frames from the phase vocoder + new_grain.norm[:] = 0 + new_grain.phas[:] = 0 + samples = p.rdo(new_grain) + sink_out(samples, read if t == warmup else hop_s) + +# just to make sure +#sink_out.close() + +format_out = "read {:d} blocks from {:s} at {:d}Hz and rate {:f}, wrote {:d} blocks to {:s}" +print (format_out.format(block_read, source_filename, samplerate, rate, + len(steps), output_filename)) diff --git a/python/demos/demo_timestretch_online.py b/python/demos/demo_timestretch_online.py new file mode 100755 index 00000000..df703653 --- /dev/null +++ b/python/demos/demo_timestretch_online.py @@ -0,0 +1,112 @@ +#! /usr/bin/env python + +# Implementation of the timescale algorithm according to Dan Ellis, *A Phase +# Vocoder in Matlab*. http://www.ee.columbia.edu/~dpwe/resources/matlab/pvoc/ + +# This file performs both analysis and synthesis in a single pass. See also +# `demo_timestretch.py` for a version following the original implementation. + +import sys +from aubio import source, sink, pvoc, cvec +from aubio import unwrap2pi, float_type +import numpy as np + +win_s = 512 +hop_s = win_s // 8 # 87.5 % overlap + +warmup = win_s // hop_s - 1 + +if len(sys.argv) < 3: + print("Usage: {:s} [samplerate]".format(sys.argv[0])) + print("""Examples: + # twice faster + {0} track_01.mp3 track_01_faster.wav 2.0 + # twice slower + {0} track_02.flac track_02_slower.wav 0.5 + # one and a half time faster, resampling first the input to 22050 + {0} track_02.flac track_02_slower.wav 1.5 22050""".format(sys.argv[0])) + sys.exit(1) + +source_filename = sys.argv[1] +output_filename = sys.argv[2] +rate = float(sys.argv[3]) + +samplerate = 0 if len(sys.argv) < 5 else int(sys.argv[4]) +source_in = source(source_filename, samplerate, hop_s) +samplerate = source_in.samplerate +p = pvoc(win_s, hop_s) + +sink_out = sink(output_filename, samplerate) + +# excepted phase advance in each bin +phi_advance = np.linspace(0, np.pi * hop_s, win_s / 2 + 1).astype (float_type) + +old_grain = cvec(win_s) +new_grain = cvec(win_s) + +block_read = 0 +interp_read = 0 +interp_block = 0 +while True: + + samples, read = source_in() + cur_grain = p(samples) + + if block_read == 1: + phas_acc = old_grain.phas + + #print "block_read", block_read + while True and (block_read > 0): + if interp_read >= block_read: + break + #print "`--- interp_block:", interp_block, + #print 'at orig_block', interp_read, '<- from', block_read - 1, block_read, + #print 'old_grain', old_grain, 'cur_grain', cur_grain + # time to compute interp grain + frac = 1. - np.mod(interp_read, 1.0) + + # compute interpolated frame + new_grain.norm = frac * old_grain.norm + (1. - frac) * cur_grain.norm + new_grain.phas = phas_acc + + # psola + samples = p.rdo(new_grain) + if interp_read > warmup: # skip the first frames to warm up phase vocoder + # write to sink + sink_out(samples, hop_s) + + # calculate phase advance + dphas = cur_grain.phas - old_grain.phas - phi_advance + # unwrap angle to [-pi; pi] + dphas = unwrap2pi(dphas) + # cumulate phase, to be used for next frame + phas_acc += phi_advance + dphas + + # prepare for next interp block + interp_block += 1 + interp_read = interp_block * rate + if interp_read >= block_read: + break + + # copy cur_grain to old_grain + old_grain.norm = np.copy(cur_grain.norm) + old_grain.phas = np.copy(cur_grain.phas) + + # until end of file + if read < hop_s: break + # increment block counter + block_read += 1 + +for t in range(warmup + 2): # purge the last frames from the phase vocoder + new_grain.norm[:] = 0 + new_grain.phas[:] = 0 + samples = p.rdo(new_grain) + sink_out(samples, read if t == warmup + 1 else hop_s) + +# just to make sure +source_in.close() +sink_out.close() + +format_out = "read {:d} blocks from {:s} at {:d}Hz and rate {:f}, wrote {:d} blocks to {:s}" +print (format_out.format(block_read, source_filename, samplerate, rate, + interp_block, output_filename)) diff --git a/python/demos/demo_tss.py b/python/demos/demo_tss.py index 884dc9b5..1a56b4f0 100755 --- a/python/demos/demo_tss.py +++ b/python/demos/demo_tss.py @@ -4,44 +4,46 @@ import sys from aubio import source, sink, pvoc, tss if __name__ == '__main__': - if len(sys.argv) < 2: - print 'usage: %s ' % sys.argv[0] - sys.exit(1) - - samplerate = 44100 - win_s = 1024 # fft size - hop_s = win_s / 4 # block size - threshold = 0.5 - - f = source(sys.argv[1], samplerate, hop_s) - g = sink(sys.argv[2], samplerate) - h = sink(sys.argv[3], samplerate) - - pva = pvoc(win_s, hop_s) # a phase vocoder - pvb = pvoc(win_s, hop_s) # another phase vocoder - t = tss(win_s, hop_s) # transient steady state separation - - t.set_threshold(threshold) - - read = hop_s - - while read: - samples, read = f() # read file - spec = pva(samples) # compute spectrum - trans_spec, stead_spec = t(spec) # transient steady-state separation - transients = pva.rdo(trans_spec) # overlap-add synthesis of transients - steadstate = pvb.rdo(stead_spec) # overlap-add synthesis of steady states - g(transients, read) # write transients to output - h(steadstate, read) # write steady states to output - - del f, g, h # finish writing the files now - - from demo_spectrogram import get_spectrogram - from pylab import subplot, show - subplot(311) - get_spectrogram(sys.argv[1]) - subplot(312) - get_spectrogram(sys.argv[2]) - subplot(313) - get_spectrogram(sys.argv[3]) - show() + if len(sys.argv) < 2: + print('usage: %s ' % sys.argv[0]) + sys.exit(1) + + samplerate = 44100 + win_s = 1024 # fft size + hop_s = win_s // 8 # block size + + f = source(sys.argv[1], samplerate, hop_s) + g = sink(sys.argv[2], samplerate) + h = sink(sys.argv[3], samplerate) + + pva = pvoc(win_s, hop_s) # a phase vocoder + pvb = pvoc(win_s, hop_s) # another phase vocoder + t = tss(win_s, hop_s) # transient steady state separation + + t.set_threshold(0.01) + t.set_alpha(3.) + t.set_beta(4.) + + read = hop_s + + while read: + samples, read = f() # read file + spec = pva(samples) # compute spectrum + trans_spec, stead_spec = t(spec) # transient steady-state separation + transients = pva.rdo(trans_spec) # overlap-add synthesis of transients + steadstate = pvb.rdo(stead_spec) # overlap-add synthesis of steady states + g(transients, read) # write transients to output + h(steadstate, read) # write steady states to output + + del f, g, h # finish writing the files now + sys.exit(0) + + from demo_spectrogram import get_spectrogram + from pylab import subplot, show + subplot(311) + get_spectrogram(sys.argv[1]) + subplot(312) + get_spectrogram(sys.argv[2]) + subplot(313) + get_spectrogram(sys.argv[3]) + show() diff --git a/python/demos/demo_waveform_plot.py b/python/demos/demo_waveform_plot.py index 91c6eda2..54342239 100755 --- a/python/demos/demo_waveform_plot.py +++ b/python/demos/demo_waveform_plot.py @@ -1,7 +1,7 @@ #! /usr/bin/env python import sys -from aubio import pvoc, source +from aubio import source from numpy import zeros, hstack def get_waveform_plot(filename, samplerate = 0, block_size = 4096, ax = None, downsample = 2**4): @@ -21,7 +21,7 @@ def get_waveform_plot(filename, samplerate = 0, block_size = 4096, ax = None, do while True: samples, read = a() # keep some data to plot it later - new_maxes = (abs(samples.reshape(hop_s/downsample, downsample))).max(axis=0) + new_maxes = (abs(samples.reshape(hop_s//downsample, downsample))).max(axis=0) allsamples_max = hstack([allsamples_max, new_maxes]) total_frames += read if read < hop_s: break @@ -48,7 +48,7 @@ def set_xlabels_sample2time(ax, latest_sample, samplerate): if __name__ == '__main__': import matplotlib.pyplot as plt if len(sys.argv) < 2: - print "Usage: %s " % sys.argv[0] + print("Usage: %s " % sys.argv[0]) else: for soundfile in sys.argv[1:]: get_waveform_plot(soundfile) diff --git a/python/ext/aubio-types.h b/python/ext/aubio-types.h index 280d01b7..26f8b1d6 100644 --- a/python/ext/aubio-types.h +++ b/python/ext/aubio-types.h @@ -1,6 +1,8 @@ #include #include +#include "aubio-generated.h" + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION // define numpy unique symbols for aubio @@ -25,7 +27,7 @@ #ifdef USE_LOCAL_AUBIO #include "aubio.h" #else -#include "aubio/aubio.h" +#include #endif #define Py_default_vector_length 1024 @@ -33,31 +35,46 @@ #define Py_aubio_default_samplerate 44100 #if HAVE_AUBIO_DOUBLE -#error "Ouch! Python interface for aubio has not been much tested yet." +// 64 bit precision with HAVE_AUBIO_DOUBLE=1 #define AUBIO_NPY_SMPL NPY_DOUBLE +#define AUBIO_NPY_SMPL_STR "float64" +#define AUBIO_NPY_SMPL_CHR "d" #else +// default is 32 bit precision #define AUBIO_NPY_SMPL NPY_FLOAT +#define AUBIO_NPY_SMPL_STR "float32" +#define AUBIO_NPY_SMPL_CHR "f" +#endif + +#ifndef PATH_MAX +#ifdef MAX_PATH +#define PATH_MAX MAX_PATH +#else +#define PATH_MAX 1024 +#endif +#endif + +// compat with Python < 2.6 +#ifndef Py_TYPE +#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #endif -// special python type for cvec -typedef struct -{ - PyObject_HEAD - cvec_t * o; - uint_t length; - uint_t channels; -} Py_cvec; extern PyTypeObject Py_cvecType; +PyObject * new_py_fvec(uint_t length); +PyObject * new_py_cvec(uint_t length); +PyObject * new_py_fmat(uint_t height, uint_t length); + // defined in aubio-proxy.c +extern int PyAubio_IsValidVector (PyObject *input); + extern PyObject *PyAubio_CFvecToArray (fvec_t * self); -extern fvec_t *PyAubio_ArrayToCFvec (PyObject * self); +extern int PyAubio_ArrayToCFvec (PyObject * self, fvec_t *out); -extern Py_cvec *PyAubio_CCvecToPyCvec (cvec_t * self); -extern cvec_t *PyAubio_ArrayToCCvec (PyObject *input); +extern int PyAubio_PyCvecToCCvec (PyObject *input, cvec_t *i); extern PyObject *PyAubio_CFmatToArray (fmat_t * self); -extern fmat_t *PyAubio_ArrayToCFmat (PyObject *input); +extern int PyAubio_ArrayToCFmat (PyObject *input, fmat_t *out); // hand written wrappers extern PyTypeObject Py_filterType; diff --git a/python/ext/aubiomodule.c b/python/ext/aubiomodule.c index 8278db2d..76ed9c9b 100644 --- a/python/ext/aubiomodule.c +++ b/python/ext/aubiomodule.c @@ -1,8 +1,12 @@ #define PY_AUBIO_MODULE_MAIN #include "aubio-types.h" -#include "aubio-generated.h" #include "py-musicutils.h" +// this dummy macro is used to convince windows that a string passed as -D flag +// is just that, a string, and not a double. +#define REDEFINESTRING(x) #x +#define DEFINEDSTRING(x) REDEFINESTRING(x) + static char aubio_module_doc[] = "Python module for the aubio library"; static char Py_alpha_norm_doc[] = "" @@ -75,7 +79,6 @@ static char Py_min_removal_doc[] = "" "\n" ">>> min_removal(a)"; -extern void add_generated_objects ( PyObject *m ); extern void add_ufuncs ( PyObject *m ); extern int generated_types_ready(void); @@ -83,11 +86,11 @@ static PyObject * Py_alpha_norm (PyObject * self, PyObject * args) { PyObject *input; - fvec_t *vec; + fvec_t vec; smpl_t alpha; PyObject *result; - if (!PyArg_ParseTuple (args, "Of:alpha_norm", &input, &alpha)) { + if (!PyArg_ParseTuple (args, "O" AUBIO_NPY_SMPL_CHR ":alpha_norm", &input, &alpha)) { return NULL; } @@ -95,14 +98,12 @@ Py_alpha_norm (PyObject * self, PyObject * args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &vec)) { return NULL; } // compute the function - result = Py_BuildValue ("f", fvec_alpha_norm (vec, alpha)); + result = Py_BuildValue (AUBIO_NPY_SMPL_CHR, fvec_alpha_norm (&vec, alpha)); if (result == NULL) { return NULL; } @@ -116,7 +117,7 @@ Py_bintomidi (PyObject * self, PyObject * args) smpl_t input, samplerate, fftsize; smpl_t output; - if (!PyArg_ParseTuple (args, "|fff", &input, &samplerate, &fftsize)) { + if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) { return NULL; } @@ -131,7 +132,7 @@ Py_miditobin (PyObject * self, PyObject * args) smpl_t input, samplerate, fftsize; smpl_t output; - if (!PyArg_ParseTuple (args, "|fff", &input, &samplerate, &fftsize)) { + if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) { return NULL; } @@ -146,7 +147,7 @@ Py_bintofreq (PyObject * self, PyObject * args) smpl_t input, samplerate, fftsize; smpl_t output; - if (!PyArg_ParseTuple (args, "|fff", &input, &samplerate, &fftsize)) { + if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) { return NULL; } @@ -161,7 +162,7 @@ Py_freqtobin (PyObject * self, PyObject * args) smpl_t input, samplerate, fftsize; smpl_t output; - if (!PyArg_ParseTuple (args, "|fff", &input, &samplerate, &fftsize)) { + if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) { return NULL; } @@ -174,7 +175,7 @@ static PyObject * Py_zero_crossing_rate (PyObject * self, PyObject * args) { PyObject *input; - fvec_t *vec; + fvec_t vec; PyObject *result; if (!PyArg_ParseTuple (args, "O:zero_crossing_rate", &input)) { @@ -185,14 +186,12 @@ Py_zero_crossing_rate (PyObject * self, PyObject * args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &vec)) { return NULL; } // compute the function - result = Py_BuildValue ("f", aubio_zero_crossing_rate (vec)); + result = Py_BuildValue (AUBIO_NPY_SMPL_CHR, aubio_zero_crossing_rate (&vec)); if (result == NULL) { return NULL; } @@ -204,7 +203,7 @@ static PyObject * Py_min_removal(PyObject * self, PyObject * args) { PyObject *input; - fvec_t *vec; + fvec_t vec; if (!PyArg_ParseTuple (args, "O:min_removal", &input)) { return NULL; @@ -214,19 +213,17 @@ Py_min_removal(PyObject * self, PyObject * args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &vec)) { return NULL; } // compute the function - fvec_min_removal (vec); + fvec_min_removal (&vec); // since this function does not return, we could return None //Py_RETURN_NONE; // however it is convenient to return the modified vector - return (PyObject *) PyAubio_CFvecToArray(vec); + return (PyObject *) PyAubio_CFvecToArray(&vec); // or even without converting it back to an array //Py_INCREF(vec); //return (PyObject *)vec; @@ -245,13 +242,44 @@ static PyMethodDef aubio_methods[] = { {"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}, - {NULL, NULL} /* Sentinel */ + {NULL, NULL, 0, NULL} /* Sentinel */ }; -PyMODINIT_FUNC -init_aubio (void) +#if PY_MAJOR_VERSION >= 3 +// Python3 module definition +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_aubio", /* m_name */ + aubio_module_doc, /* m_doc */ + -1, /* m_size */ + aubio_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + +void +aubio_log_function(int level, const char *message, void *data) { - PyObject *m; + // remove trailing \n + char *pos; + if ((pos=strchr(message, '\n')) != NULL) { + *pos = '\0'; + } + // warning or error + if (level == AUBIO_LOG_ERR) { + PyErr_Format(PyExc_RuntimeError, "%s", message); + } else { + PyErr_WarnEx(PyExc_UserWarning, message, 1); + } +} + +static PyObject * +initaubio (void) +{ + PyObject *m = NULL; int err; // fvec is defined in __init__.py @@ -265,13 +293,17 @@ init_aubio (void) // generated objects || (generated_types_ready() < 0 ) ) { - return; + return m; } +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&moduledef); +#else m = Py_InitModule3 ("_aubio", aubio_methods, aubio_module_doc); +#endif if (m == NULL) { - return; + return m; } err = _import_array (); @@ -295,9 +327,30 @@ init_aubio (void) Py_INCREF (&Py_sinkType); PyModule_AddObject (m, "sink", (PyObject *) & Py_sinkType); + PyModule_AddStringConstant(m, "float_type", AUBIO_NPY_SMPL_STR); + PyModule_AddStringConstant(m, "__version__", DEFINEDSTRING(AUBIO_VERSION)); + // add generated objects add_generated_objects(m); // add ufunc add_ufuncs(m); + + aubio_log_set_level_function(AUBIO_LOG_ERR, aubio_log_function, NULL); + aubio_log_set_level_function(AUBIO_LOG_WRN, aubio_log_function, NULL); + return m; } + +#if PY_MAJOR_VERSION >= 3 + // Python3 init + PyMODINIT_FUNC PyInit__aubio(void) + { + return initaubio(); + } +#else + // Python 2 init + PyMODINIT_FUNC init_aubio(void) + { + initaubio(); + } +#endif diff --git a/python/ext/aubioproxy.c b/python/ext/aubioproxy.c index d3c00949..f5d7fa48 100644 --- a/python/ext/aubioproxy.c +++ b/python/ext/aubioproxy.c @@ -1,12 +1,30 @@ #include "aubio-types.h" -fvec_t * -PyAubio_ArrayToCFvec (PyObject *input) { - PyObject *array; - fvec_t *vec; +PyObject * +new_py_fvec(uint_t length) { + npy_intp dims[] = { length, 1 }; + return PyArray_ZEROS(1, dims, AUBIO_NPY_SMPL, 0); +} + +PyObject * +new_py_fmat(uint_t height, uint_t length) { + npy_intp dims[] = { height, length, 1 }; + return PyArray_ZEROS(2, dims, AUBIO_NPY_SMPL, 0); +} + +PyObject * +PyAubio_CFvecToArray (fvec_t * self) +{ + npy_intp dims[] = { self->length, 1 }; + return PyArray_SimpleNewFromData (1, dims, AUBIO_NPY_SMPL, self->data); +} + +int +PyAubio_IsValidVector (PyObject * input) { + npy_intp length; if (input == NULL) { PyErr_SetString (PyExc_ValueError, "input array is not a python object"); - goto fail; + return 0; } // parsing input object into a Py_fvec if (PyArray_Check(input)) { @@ -14,74 +32,47 @@ PyAubio_ArrayToCFvec (PyObject *input) { // we got an array, convert it to an fvec if (PyArray_NDIM ((PyArrayObject *)input) == 0) { PyErr_SetString (PyExc_ValueError, "input array is a scalar"); - goto fail; + return 0; } else if (PyArray_NDIM ((PyArrayObject *)input) > 1) { PyErr_SetString (PyExc_ValueError, "input array has more than one dimensions"); - goto fail; + return 0; } if (!PyArray_ISFLOAT ((PyArrayObject *)input)) { PyErr_SetString (PyExc_ValueError, "input array should be float"); - goto fail; + return 0; } else if (PyArray_TYPE ((PyArrayObject *)input) != AUBIO_NPY_SMPL) { - PyErr_SetString (PyExc_ValueError, "input array should be float32"); - goto fail; - } else { - // input data type is float32, nothing else to do - array = input; + PyErr_SetString (PyExc_ValueError, "input array should be " AUBIO_NPY_SMPL_STR); + return 0; } - // vec = new_fvec (vec->length); - // no need to really allocate fvec, just its struct member - vec = (fvec_t *)malloc(sizeof(fvec_t)); - long length = PyArray_SIZE ((PyArrayObject *)array); - if (length > 0) { - vec->length = (uint_t)length; - } else { + length = PyArray_SIZE ((PyArrayObject *)input); + if (length <= 0) { PyErr_SetString (PyExc_ValueError, "input array size should be greater than 0"); - goto fail; + return 0; } - vec->data = (smpl_t *) PyArray_GETPTR1 ((PyArrayObject *)array, 0); } else if (PyObject_TypeCheck (input, &PyList_Type)) { PyErr_SetString (PyExc_ValueError, "does not convert from list yet"); - return NULL; + return 0; } else { PyErr_SetString (PyExc_ValueError, "can only accept vector of float as input"); - return NULL; + return 0; } - - return vec; - -fail: - return NULL; + return 1; } -PyObject * -PyAubio_CFvecToArray (fvec_t * self) -{ - npy_intp dims[] = { self->length, 1 }; - return PyArray_SimpleNewFromData (1, dims, AUBIO_NPY_SMPL, self->data); -} - -Py_cvec * -PyAubio_CCvecToPyCvec (cvec_t * input) { - Py_cvec *vec = (Py_cvec*) PyObject_New (Py_cvec, &Py_cvecType); - vec->length = input->length; - vec->o = input; - Py_INCREF(vec); - return vec; -} +int +PyAubio_ArrayToCFvec (PyObject *input, fvec_t *out) { -cvec_t * -PyAubio_ArrayToCCvec (PyObject *input) { - if (PyObject_TypeCheck (input, &Py_cvecType)) { - return ((Py_cvec*)input)->o; - } else { - PyErr_SetString (PyExc_ValueError, "input array should be float32"); - return NULL; + if (!PyAubio_IsValidVector(input)){ + return 0; } + + out->length = (uint_t) PyArray_SIZE ((PyArrayObject *)input); + out->data = (smpl_t *) PyArray_GETPTR1 ((PyArrayObject *)input, 0); + return 1; } PyObject * @@ -101,14 +92,13 @@ PyAubio_CFmatToArray (fmat_t * input) return array; } -fmat_t * -PyAubio_ArrayToCFmat (PyObject *input) { - PyObject *array; - fmat_t *mat; - uint_t i; +int +PyAubio_ArrayToCFmat (PyObject *input, fmat_t *mat) { + uint_t i, new_height; + npy_intp length, height; if (input == NULL) { PyErr_SetString (PyExc_ValueError, "input array is not a python object"); - goto fail; + return 0; } // parsing input object into a Py_fvec if (PyArray_Check(input)) { @@ -116,56 +106,53 @@ PyAubio_ArrayToCFmat (PyObject *input) { // we got an array, convert it to an fvec if (PyArray_NDIM ((PyArrayObject *)input) == 0) { PyErr_SetString (PyExc_ValueError, "input array is a scalar"); - goto fail; + return 0; } else if (PyArray_NDIM ((PyArrayObject *)input) > 2) { PyErr_SetString (PyExc_ValueError, "input array has more than two dimensions"); - goto fail; + return 0; } if (!PyArray_ISFLOAT ((PyArrayObject *)input)) { PyErr_SetString (PyExc_ValueError, "input array should be float"); - goto fail; + return 0; } else if (PyArray_TYPE ((PyArrayObject *)input) != AUBIO_NPY_SMPL) { - PyErr_SetString (PyExc_ValueError, "input array should be float32"); - goto fail; - } else { - // input data type is float32, nothing else to do - array = input; + PyErr_SetString (PyExc_ValueError, "input array should be " AUBIO_NPY_SMPL_STR); + return 0; } // no need to really allocate fvec, just its struct member - mat = (fmat_t *)malloc(sizeof(fmat_t)); - long length = PyArray_DIM ((PyArrayObject *)array, 1); - if (length > 0) { - mat->length = (uint_t)length; - } else { + length = PyArray_DIM ((PyArrayObject *)input, 1); + if (length <= 0) { PyErr_SetString (PyExc_ValueError, "input array dimension 1 should be greater than 0"); - goto fail; + return 0; } - long height = PyArray_DIM ((PyArrayObject *)array, 0); - if (height > 0) { - mat->height = (uint_t)height; - } else { + height = PyArray_DIM ((PyArrayObject *)input, 0); + if (height <= 0) { PyErr_SetString (PyExc_ValueError, "input array dimension 0 should be greater than 0"); - goto fail; - } - mat->data = (smpl_t **)malloc(sizeof(smpl_t*) * mat->height); - for (i=0; i< mat->height; i++) { - mat->data[i] = (smpl_t*)PyArray_GETPTR1 ((PyArrayObject *)array, i); + return 0; } } else if (PyObject_TypeCheck (input, &PyList_Type)) { PyErr_SetString (PyExc_ValueError, "can not convert list to fmat"); - return NULL; + return 0; } else { PyErr_SetString (PyExc_ValueError, "can only accept matrix of float as input"); - return NULL; + return 0; } - return mat; + new_height = (uint_t)PyArray_DIM ((PyArrayObject *)input, 0); + if (mat->height != new_height) { + if (mat->data) { + free(mat->data); + } + mat->data = (smpl_t **)malloc(sizeof(smpl_t*) * new_height); + } -fail: - return NULL; + mat->height = new_height; + mat->length = (uint_t)PyArray_DIM ((PyArrayObject *)input, 1); + for (i=0; i< mat->height; i++) { + mat->data[i] = (smpl_t*)PyArray_GETPTR1 ((PyArrayObject *)input, i); + } + return 1; } - diff --git a/python/ext/aubiowraphell.h b/python/ext/aubiowraphell.h deleted file mode 100644 index d60cc30a..00000000 --- a/python/ext/aubiowraphell.h +++ /dev/null @@ -1,90 +0,0 @@ -#include "aubio-types.h" - -#define AUBIO_DECLARE(NAME, PARAMS...) \ -typedef struct { \ - PyObject_HEAD \ - aubio_ ## NAME ## _t * o; \ - PARAMS; \ -} Py_## NAME; - -#define AUBIO_INIT(NAME, PARAMS... ) \ -static int \ -Py_ ## NAME ## _init (Py_ ## NAME * self, PyObject * args, PyObject * kwds) \ -{ \ - self->o = new_aubio_## NAME ( PARAMS ); \ - if (self->o == NULL) { \ - PyErr_SetString (PyExc_StandardError, "error creating object"); \ - return -1; \ - } \ -\ - return 0; \ -} - -#define AUBIO_DEL(NAME) \ -static void \ -Py_ ## NAME ## _del ( Py_ ## NAME * self) \ -{ \ - del_aubio_ ## NAME (self->o); \ - self->ob_type->tp_free ((PyObject *) self); \ -} - -#define AUBIO_MEMBERS_START(NAME) \ -static PyMemberDef Py_ ## NAME ## _members[] = { - -#define AUBIO_MEMBERS_STOP(NAME) \ - {NULL} \ -}; - -#define AUBIO_METHODS(NAME) \ -static PyMethodDef Py_ ## NAME ## _methods[] = { \ - {NULL} \ -}; - - -#define AUBIO_TYPEOBJECT(NAME, PYNAME) \ -PyTypeObject Py_ ## NAME ## Type = { \ - PyObject_HEAD_INIT (NULL) \ - 0, \ - PYNAME, \ - sizeof (Py_ ## NAME), \ - 0, \ - (destructor) Py_ ## NAME ## _del, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - (ternaryfunc)Py_ ## NAME ## _do, \ - 0, \ - 0, \ - 0, \ - 0, \ - Py_TPFLAGS_DEFAULT, \ - Py_ ## NAME ## _doc, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - Py_ ## NAME ## _methods, \ - Py_ ## NAME ## _members, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - (initproc) Py_ ## NAME ## _init, \ - 0, \ - Py_ ## NAME ## _new, \ -}; - -// some more helpers -#define AUBIO_NEW_VEC(name, type, lengthval) \ - name = (type *) PyObject_New (type, & type ## Type); \ - name->length = lengthval; diff --git a/python/ext/py-cvec.c b/python/ext/py-cvec.c index 940508fb..a393c4e2 100644 --- a/python/ext/py-cvec.c +++ b/python/ext/py-cvec.c @@ -1,17 +1,51 @@ #include "aubio-types.h" -/* cvec type definition +/* cvec type definition class cvec(): - def __init__(self, length = 1024): - self.length = length - self.norm = array(length) - self.phas = array(length) + def __new__(self, length = 1024): + self.length = length / 2 + 1 + self.norm = np.zeros(length / 2 + 1) + self.phas = np.zeros(length / 2 + 1) */ +// special python type for cvec +typedef struct +{ + PyObject_HEAD + PyObject *norm; + PyObject *phas; + uint_t length; +} Py_cvec; + static char Py_cvec_doc[] = "cvec object"; + +PyObject * +new_py_cvec(uint_t length) { + Py_cvec* vec = (Py_cvec*) PyObject_New (Py_cvec, &Py_cvecType); + npy_intp dims[] = { length / 2 + 1, 1 }; + vec->norm = PyArray_ZEROS(1, dims, AUBIO_NPY_SMPL, 0); + vec->phas = PyArray_ZEROS(1, dims, AUBIO_NPY_SMPL, 0); + vec->length = length / 2 + 1; + return (PyObject*)vec; +} + +int +PyAubio_PyCvecToCCvec (PyObject *input, cvec_t *i) { + if (PyObject_TypeCheck (input, &Py_cvecType)) { + Py_cvec * in = (Py_cvec *)input; + i->norm = (smpl_t *) PyArray_GETPTR1 ((PyArrayObject *)(in->norm), 0); + i->phas = (smpl_t *) PyArray_GETPTR1 ((PyArrayObject *)(in->phas), 0); + i->length = ((Py_cvec*)input)->length; + return 1; + } else { + PyErr_SetString (PyExc_ValueError, "input array should be aubio.cvec"); + return 0; + } +} + static PyObject * Py_cvec_new (PyTypeObject * type, PyObject * args, PyObject * kwds) { @@ -24,7 +58,6 @@ Py_cvec_new (PyTypeObject * type, PyObject * args, PyObject * kwds) return NULL; } - self = (Py_cvec *) type->tp_alloc (type, 0); self->length = Py_default_vector_length / 2 + 1; @@ -47,19 +80,18 @@ Py_cvec_new (PyTypeObject * type, PyObject * args, PyObject * kwds) static int Py_cvec_init (Py_cvec * self, PyObject * args, PyObject * kwds) { - self->o = new_cvec ((self->length - 1) * 2); - if (self->o == NULL) { - return -1; - } - + npy_intp dims[] = { self->length, 1 }; + self->phas = PyArray_ZEROS(1, dims, AUBIO_NPY_SMPL, 0); + self->norm = PyArray_ZEROS(1, dims, AUBIO_NPY_SMPL, 0); return 0; } static void Py_cvec_del (Py_cvec * self) { - del_cvec (self->o); - self->ob_type->tp_free ((PyObject *) self); + Py_DECREF(self->norm); + Py_DECREF(self->phas); + Py_TYPE(self)->tp_free ((PyObject *) self); } static PyObject * @@ -69,7 +101,7 @@ Py_cvec_repr (Py_cvec * self, PyObject * unused) PyObject *args = NULL; PyObject *result = NULL; - format = PyString_FromString ("aubio cvec of %d elements"); + format = PyUnicode_FromString ("aubio cvec of %d elements"); if (format == NULL) { goto fail; } @@ -78,9 +110,9 @@ Py_cvec_repr (Py_cvec * self, PyObject * unused) if (args == NULL) { goto fail; } - cvec_print ( self->o ); + // hide actual norm / phas content - result = PyString_Format (format, args); + result = PyUnicode_Format (format, args); fail: Py_XDECREF (format); @@ -90,152 +122,61 @@ fail: } PyObject * -PyAubio_CvecNormToArray (Py_cvec * self) -{ - npy_intp dims[] = { self->o->length, 1 }; - return PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->norm); -} - - -PyObject * -PyAubio_CvecPhasToArray (Py_cvec * self) -{ - npy_intp dims[] = { self->o->length, 1 }; - return PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->phas); -} - -PyObject * -PyAubio_ArrayToCvecPhas (PyObject * self) -{ - return NULL; -} - -PyObject * Py_cvec_get_norm (Py_cvec * self, void *closure) { - return PyAubio_CvecNormToArray(self); + // we want self->norm to still exist after our caller return it + Py_INCREF(self->norm); + return (PyObject*)(self->norm); } PyObject * Py_cvec_get_phas (Py_cvec * self, void *closure) { - return PyAubio_CvecPhasToArray(self); + // we want self->phas to still exist after our caller return it + Py_INCREF(self->phas); + return (PyObject *)(self->phas); } static int Py_cvec_set_norm (Py_cvec * vec, PyObject *input, void * closure) { - PyArrayObject * array; - if (input == NULL) { - PyErr_SetString (PyExc_ValueError, "input array is not a python object"); - goto fail; + npy_intp length; + if (!PyAubio_IsValidVector(input)) { + return 1; } - if (PyArray_Check(input)) { - - // we got an array, convert it to a cvec.norm - if (PyArray_NDIM ((PyArrayObject *)input) == 0) { - PyErr_SetString (PyExc_ValueError, "input array is a scalar"); - goto fail; - } else if (PyArray_NDIM ((PyArrayObject *)input) > 2) { - PyErr_SetString (PyExc_ValueError, - "input array has more than two dimensions"); - goto fail; - } - - if (!PyArray_ISFLOAT ((PyArrayObject *)input)) { - PyErr_SetString (PyExc_ValueError, "input array should be float"); - goto fail; - } else if (PyArray_TYPE ((PyArrayObject *)input) != AUBIO_NPY_SMPL) { - PyErr_SetString (PyExc_ValueError, "input array should be float32"); - goto fail; - } - array = (PyArrayObject *)input; - - // check input array dimensions - if (PyArray_NDIM (array) != 1) { - PyErr_Format (PyExc_ValueError, - "input array has %d dimensions, not 1", - PyArray_NDIM (array)); - goto fail; - } else { - if (vec->o->length != PyArray_SIZE (array)) { - PyErr_Format (PyExc_ValueError, - "input array has length %d, but cvec has length %d", - (int)PyArray_SIZE (array), vec->o->length); - goto fail; - } - } - - vec->o->norm = (smpl_t *) PyArray_GETPTR1 (array, 0); - - } else { - PyErr_SetString (PyExc_ValueError, "can only accept array as input"); + length = PyArray_SIZE ((PyArrayObject *)input); + if (length != vec->length) { + PyErr_Format (PyExc_ValueError, + "input array has length %" NPY_INTP_FMT ", but cvec has length %d", length, + vec->length); return 1; } - Py_INCREF(array); + Py_XDECREF(vec->norm); + vec->norm = input; + Py_INCREF(vec->norm); return 0; - -fail: - return 1; } static int Py_cvec_set_phas (Py_cvec * vec, PyObject *input, void * closure) { - PyArrayObject * array; - if (input == NULL) { - PyErr_SetString (PyExc_ValueError, "input array is not a python object"); - goto fail; + npy_intp length; + if (!PyAubio_IsValidVector(input)) { + return 1; } - if (PyArray_Check(input)) { - - // we got an array, convert it to a cvec.phas - if (PyArray_NDIM ((PyArrayObject *)input) == 0) { - PyErr_SetString (PyExc_ValueError, "input array is a scalar"); - goto fail; - } else if (PyArray_NDIM ((PyArrayObject *)input) > 2) { - PyErr_SetString (PyExc_ValueError, - "input array has more than two dimensions"); - goto fail; - } - - if (!PyArray_ISFLOAT ((PyArrayObject *)input)) { - PyErr_SetString (PyExc_ValueError, "input array should be float"); - goto fail; - } else if (PyArray_TYPE ((PyArrayObject *)input) != AUBIO_NPY_SMPL) { - PyErr_SetString (PyExc_ValueError, "input array should be float32"); - goto fail; - } - array = (PyArrayObject *)input; - - // check input array dimensions - if (PyArray_NDIM (array) != 1) { - PyErr_Format (PyExc_ValueError, - "input array has %d dimensions, not 1", - PyArray_NDIM (array)); - goto fail; - } else { - if (vec->o->length != PyArray_SIZE (array)) { - PyErr_Format (PyExc_ValueError, - "input array has length %d, but cvec has length %d", - (int)PyArray_SIZE (array), vec->o->length); - goto fail; - } - } - - vec->o->phas = (smpl_t *) PyArray_GETPTR1 (array, 0); - - } else { - PyErr_SetString (PyExc_ValueError, "can only accept array as input"); + length = PyArray_SIZE ((PyArrayObject *)input); + if (length != vec->length) { + PyErr_Format (PyExc_ValueError, + "input array has length %" NPY_INTP_FMT ", but cvec has length %d", length, + vec->length); return 1; } - Py_INCREF(array); + Py_XDECREF(vec->phas); + vec->phas = input; + Py_INCREF(vec->phas); return 0; - -fail: - return 1; } static PyMemberDef Py_cvec_members[] = { @@ -260,8 +201,7 @@ static PyGetSetDef Py_cvec_getseters[] = { }; PyTypeObject Py_cvecType = { - PyObject_HEAD_INIT (NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "aubio.cvec", /* tp_name */ sizeof (Py_cvec), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -272,7 +212,7 @@ PyTypeObject Py_cvecType = { 0, /* tp_compare */ (reprfunc) Py_cvec_repr, /* tp_repr */ 0, /* tp_as_number */ - 0, //&Py_cvec_tp_as_sequence, /* tp_as_sequence */ + 0, //&Py_cvec_tp_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ @@ -299,4 +239,13 @@ PyTypeObject Py_cvecType = { (initproc) Py_cvec_init, /* tp_init */ 0, /* tp_alloc */ Py_cvec_new, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, }; diff --git a/python/ext/py-fft.c b/python/ext/py-fft.c index 3feb5454..87636327 100644 --- a/python/ext/py-fft.c +++ b/python/ext/py-fft.c @@ -1,10 +1,20 @@ -#include "aubiowraphell.h" +#include "aubio-types.h" static char Py_fft_doc[] = "fft object"; -AUBIO_DECLARE(fft, uint_t win_s) +typedef struct +{ + PyObject_HEAD + aubio_fft_t * o; + uint_t win_s; + // do / rdo input vectors + fvec_t vecin; + cvec_t cvecin; + // do / rdo output results + PyObject *doout; + PyObject *rdoout; +} Py_fft; -//AUBIO_NEW(fft) static PyObject * Py_fft_new (PyTypeObject * type, PyObject * args, PyObject * kwds) { @@ -36,62 +46,97 @@ Py_fft_new (PyTypeObject * type, PyObject * args, PyObject * kwds) return (PyObject *) self; } +static int +Py_fft_init (Py_fft * self, PyObject * args, PyObject * kwds) +{ + self->o = new_aubio_fft (self->win_s); + if (self->o == NULL) { + // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called + // AUBIO_ERR when failing + return -1; + } + + self->doout = new_py_cvec(self->win_s); + self->rdoout = new_py_fvec(self->win_s); -AUBIO_INIT(fft, self->win_s) + return 0; +} -AUBIO_DEL(fft) +static void +Py_fft_del (Py_fft *self, PyObject *unused) +{ + Py_XDECREF(self->doout); + Py_XDECREF(self->rdoout); + if (self->o) { + del_aubio_fft(self->o); + } + Py_TYPE(self)->tp_free((PyObject *) self); +} -static PyObject * -Py_fft_do(PyObject * self, PyObject * args) +static PyObject * +Py_fft_do(Py_fft * self, PyObject * args) { PyObject *input; - fvec_t *vec; - cvec_t *output; + cvec_t c_out; if (!PyArg_ParseTuple (args, "O", &input)) { return NULL; } - vec = PyAubio_ArrayToCFvec (input); - - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &(self->vecin))) { return NULL; } - output = new_cvec(((Py_fft *) self)->win_s); + if (self->vecin.length != self->win_s) { + PyErr_Format(PyExc_ValueError, + "input array has length %d, but fft expects length %d", + self->vecin.length, self->win_s); + return NULL; + } + Py_INCREF(self->doout); + if (!PyAubio_PyCvecToCCvec(self->doout, &c_out)) { + return NULL; + } // compute the function - aubio_fft_do (((Py_fft *)self)->o, vec, output); - return (PyObject *)PyAubio_CCvecToPyCvec(output); + aubio_fft_do (self->o, &(self->vecin), &c_out); + return self->doout; } -AUBIO_MEMBERS_START(fft) +static PyMemberDef Py_fft_members[] = { {"win_s", T_INT, offsetof (Py_fft, win_s), READONLY, "size of the window"}, -AUBIO_MEMBERS_STOP(fft) + {NULL} +}; -static PyObject * +static PyObject * Py_fft_rdo(Py_fft * self, PyObject * args) { PyObject *input; - cvec_t *vec; - fvec_t *output; + fvec_t out; if (!PyArg_ParseTuple (args, "O", &input)) { return NULL; } - vec = PyAubio_ArrayToCCvec (input); - - if (vec == NULL) { + if (!PyAubio_PyCvecToCCvec (input, &(self->cvecin)) ) { return NULL; } - output = new_fvec(self->win_s); + if (self->cvecin.length != self->win_s / 2 + 1) { + PyErr_Format(PyExc_ValueError, + "input cvec has length %d, but fft expects length %d", + self->cvecin.length, self->win_s / 2 + 1); + return NULL; + } + Py_INCREF(self->rdoout); + if (!PyAubio_ArrayToCFvec(self->rdoout, &out) ) { + return NULL; + } // compute the function - aubio_fft_rdo (((Py_fft *)self)->o, vec, output); - return (PyObject *)PyAubio_CFvecToArray(output); + aubio_fft_rdo (self->o, &(self->cvecin), &out); + return self->rdoout; } static PyMethodDef Py_fft_methods[] = { @@ -100,4 +145,52 @@ static PyMethodDef Py_fft_methods[] = { {NULL} }; -AUBIO_TYPEOBJECT(fft, "aubio.fft") +PyTypeObject Py_fftType = { + PyVarObject_HEAD_INIT (NULL, 0) + "aubio.fft", + sizeof (Py_fft), + 0, + (destructor) Py_fft_del, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (ternaryfunc)Py_fft_do, + 0, + 0, + 0, + 0, + Py_TPFLAGS_DEFAULT, + Py_fft_doc, + 0, + 0, + 0, + 0, + 0, + 0, + Py_fft_methods, + Py_fft_members, + 0, + 0, + 0, + 0, + 0, + 0, + (initproc) Py_fft_init, + 0, + Py_fft_new, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; diff --git a/python/ext/py-filter.c b/python/ext/py-filter.c index 416bba83..df78e475 100644 --- a/python/ext/py-filter.c +++ b/python/ext/py-filter.c @@ -5,6 +5,9 @@ typedef struct PyObject_HEAD aubio_filter_t * o; uint_t order; + fvec_t vec; + PyObject *out; + fvec_t c_out; } Py_filter; static char Py_filter_doc[] = "filter object"; @@ -47,22 +50,22 @@ Py_filter_init (Py_filter * self, PyObject * args, PyObject * kwds) if (self->o == NULL) { return -1; } - + self->out = NULL; return 0; } static void Py_filter_del (Py_filter * self) { + Py_XDECREF(self->out); del_aubio_filter (self->o); - self->ob_type->tp_free ((PyObject *) self); + Py_TYPE(self)->tp_free ((PyObject *) self); } -static PyObject * +static PyObject * Py_filter_do(Py_filter * self, PyObject * args) { PyObject *input; - fvec_t *vec; if (!PyArg_ParseTuple (args, "O:digital_filter.do", &input)) { return NULL; @@ -72,19 +75,25 @@ Py_filter_do(Py_filter * self, PyObject * args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &(self->vec))) { return NULL; } + // initialize output now + if (self->out == NULL) { + self->out = new_py_fvec(self->vec.length); + } + + Py_INCREF(self->out); + if (!PyAubio_ArrayToCFvec(self->out, &(self->c_out)) ) { + return NULL; + } // compute the function - fvec_t * out = new_fvec(vec->length); - aubio_filter_do_outplace (self->o, vec, out); - return PyAubio_CFvecToArray(out); + aubio_filter_do_outplace (self->o, &(self->vec), &(self->c_out)); + return self->out; } -static PyObject * +static PyObject * Py_filter_set_c_weighting (Py_filter * self, PyObject *args) { uint_t err = 0; @@ -102,7 +111,7 @@ Py_filter_set_c_weighting (Py_filter * self, PyObject *args) Py_RETURN_NONE; } -static PyObject * +static PyObject * Py_filter_set_a_weighting (Py_filter * self, PyObject *args) { uint_t err = 0; @@ -156,8 +165,7 @@ static PyMethodDef Py_filter_methods[] = { }; PyTypeObject Py_filterType = { - PyObject_HEAD_INIT (NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "aubio.digital_filter", /* tp_name */ sizeof (Py_filter), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -195,4 +203,13 @@ PyTypeObject Py_filterType = { (initproc) Py_filter_init, /* tp_init */ 0, /* tp_alloc */ Py_filter_new, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, }; diff --git a/python/ext/py-filterbank.c b/python/ext/py-filterbank.c index 60e5c5d2..a4e0ea63 100644 --- a/python/ext/py-filterbank.c +++ b/python/ext/py-filterbank.c @@ -1,10 +1,20 @@ -#include "aubiowraphell.h" +#include "aubio-types.h" static char Py_filterbank_doc[] = "filterbank object"; -AUBIO_DECLARE(filterbank, uint_t n_filters; uint_t win_s) +typedef struct +{ + PyObject_HEAD + aubio_filterbank_t * o; + uint_t n_filters; + uint_t win_s; + cvec_t vec; + fvec_t freqs; + fmat_t coeffs; + PyObject *out; + fvec_t c_out; +} Py_filterbank; -//AUBIO_NEW(filterbank) static PyObject * Py_filterbank_new (PyTypeObject * type, PyObject * args, PyObject * kwds) { @@ -44,41 +54,67 @@ Py_filterbank_new (PyTypeObject * type, PyObject * args, PyObject * kwds) return (PyObject *) self; } +static int +Py_filterbank_init (Py_filterbank * self, PyObject * args, PyObject * kwds) +{ + self->o = new_aubio_filterbank (self->n_filters, self->win_s); + if (self->o == NULL) { + PyErr_Format(PyExc_RuntimeError, "error creating filterbank with" + " n_filters=%d, win_s=%d", self->n_filters, self->win_s); + return -1; + } + self->out = new_py_fvec(self->n_filters); -AUBIO_INIT(filterbank, self->n_filters, self->win_s) + return 0; +} -AUBIO_DEL(filterbank) +static void +Py_filterbank_del (Py_filterbank *self, PyObject *unused) +{ + if (self->o) { + free(self->coeffs.data); + del_aubio_filterbank(self->o); + } + Py_XDECREF(self->out); + Py_TYPE(self)->tp_free((PyObject *) self); +} static PyObject * Py_filterbank_do(Py_filterbank * self, PyObject * args) { PyObject *input; - cvec_t *vec; - fvec_t *out; if (!PyArg_ParseTuple (args, "O", &input)) { return NULL; } - vec = PyAubio_ArrayToCCvec (input); - - if (vec == NULL) { + if (!PyAubio_PyCvecToCCvec(input, &(self->vec) )) { return NULL; } - out = new_fvec (self->n_filters); + if (self->vec.length != self->win_s / 2 + 1) { + PyErr_Format(PyExc_ValueError, + "input cvec has length %d, but fft expects length %d", + self->vec.length, self->win_s / 2 + 1); + return NULL; + } + Py_INCREF(self->out); + if (!PyAubio_ArrayToCFvec(self->out, &(self->c_out))) { + return NULL; + } // compute the function - aubio_filterbank_do (self->o, vec, out); - return (PyObject *)PyAubio_CFvecToArray(out); + aubio_filterbank_do (self->o, &(self->vec), &(self->c_out)); + return self->out; } -AUBIO_MEMBERS_START(filterbank) +static PyMemberDef Py_filterbank_members[] = { {"win_s", T_INT, offsetof (Py_filterbank, win_s), READONLY, "size of the window"}, {"n_filters", T_INT, offsetof (Py_filterbank, n_filters), READONLY, "number of filters"}, -AUBIO_MEMBERS_STOP(filterbank) + {NULL} /* sentinel */ +}; static PyObject * Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) @@ -87,7 +123,6 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) PyObject *input; uint_t samplerate; - fvec_t *freqs; if (!PyArg_ParseTuple (args, "OI", &input, &samplerate)) { return NULL; } @@ -96,14 +131,12 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) return NULL; } - freqs = PyAubio_ArrayToCFvec (input); - - if (freqs == NULL) { + if (!PyAubio_ArrayToCFvec(input, &(self->freqs) )) { return NULL; } err = aubio_filterbank_set_triangle_bands (self->o, - freqs, samplerate); + &(self->freqs), samplerate); if (err > 0) { PyErr_SetString (PyExc_ValueError, "error when setting filter to A-weighting"); @@ -137,21 +170,15 @@ Py_filterbank_set_coeffs (Py_filterbank * self, PyObject *args) uint_t err = 0; PyObject *input; - fmat_t *coeffs; - if (!PyArg_ParseTuple (args, "O", &input)) { return NULL; } - coeffs = PyAubio_ArrayToCFmat (input); - - if (coeffs == NULL) { - PyErr_SetString (PyExc_ValueError, - "unable to parse input array"); + if (!PyAubio_ArrayToCFmat(input, &(self->coeffs))) { return NULL; } - err = aubio_filterbank_set_coeffs (self->o, coeffs); + err = aubio_filterbank_set_coeffs (self->o, &(self->coeffs)); if (err > 0) { PyErr_SetString (PyExc_ValueError, @@ -180,4 +207,52 @@ static PyMethodDef Py_filterbank_methods[] = { {NULL} }; -AUBIO_TYPEOBJECT(filterbank, "aubio.filterbank") +PyTypeObject Py_filterbankType = { + PyVarObject_HEAD_INIT (NULL, 0) + "aubio.filterbank", + sizeof (Py_filterbank), + 0, + (destructor) Py_filterbank_del, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (ternaryfunc)Py_filterbank_do, + 0, + 0, + 0, + 0, + Py_TPFLAGS_DEFAULT, + Py_filterbank_doc, + 0, + 0, + 0, + 0, + 0, + 0, + Py_filterbank_methods, + Py_filterbank_members, + 0, + 0, + 0, + 0, + 0, + 0, + (initproc) Py_filterbank_init, + 0, + Py_filterbank_new, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; diff --git a/python/ext/py-musicutils.c b/python/ext/py-musicutils.c index d1e3d017..3078e079 100644 --- a/python/ext/py-musicutils.c +++ b/python/ext/py-musicutils.c @@ -8,7 +8,6 @@ Py_aubio_window(PyObject *self, PyObject *args) fvec_t *window = NULL; if (!PyArg_ParseTuple (args, "|sI", &wintype, &winlen)) { - PyErr_SetString (PyExc_ValueError, "failed parsing arguments"); return NULL; } @@ -25,11 +24,10 @@ PyObject * Py_aubio_level_lin(PyObject *self, PyObject *args) { PyObject *input; - fvec_t *vec; + fvec_t vec; PyObject *level_lin; if (!PyArg_ParseTuple (args, "O:level_lin", &input)) { - PyErr_SetString (PyExc_ValueError, "failed parsing arguments"); return NULL; } @@ -37,12 +35,11 @@ Py_aubio_level_lin(PyObject *self, PyObject *args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &vec)) { return NULL; } - level_lin = Py_BuildValue("f", aubio_level_lin(vec)); + level_lin = Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_level_lin(&vec)); if (level_lin == NULL) { PyErr_SetString (PyExc_ValueError, "failed computing level_lin"); return NULL; @@ -55,11 +52,10 @@ PyObject * Py_aubio_db_spl(PyObject *self, PyObject *args) { PyObject *input; - fvec_t *vec; + fvec_t vec; PyObject *db_spl; if (!PyArg_ParseTuple (args, "O:db_spl", &input)) { - PyErr_SetString (PyExc_ValueError, "failed parsing arguments"); return NULL; } @@ -67,12 +63,11 @@ Py_aubio_db_spl(PyObject *self, PyObject *args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &vec)) { return NULL; } - db_spl = Py_BuildValue("f", aubio_db_spl(vec)); + db_spl = Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_db_spl(&vec)); if (db_spl == NULL) { PyErr_SetString (PyExc_ValueError, "failed computing db_spl"); return NULL; @@ -85,12 +80,11 @@ PyObject * Py_aubio_silence_detection(PyObject *self, PyObject *args) { PyObject *input; - fvec_t *vec; + fvec_t vec; PyObject *silence_detection; smpl_t threshold; - if (!PyArg_ParseTuple (args, "Of:silence_detection", &input, &threshold)) { - PyErr_SetString (PyExc_ValueError, "failed parsing arguments"); + if (!PyArg_ParseTuple (args, "O" AUBIO_NPY_SMPL_CHR ":silence_detection", &input, &threshold)) { return NULL; } @@ -98,12 +92,11 @@ Py_aubio_silence_detection(PyObject *self, PyObject *args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &vec)) { return NULL; } - silence_detection = Py_BuildValue("I", aubio_silence_detection(vec, threshold)); + silence_detection = Py_BuildValue("I", aubio_silence_detection(&vec, threshold)); if (silence_detection == NULL) { PyErr_SetString (PyExc_ValueError, "failed computing silence_detection"); return NULL; @@ -116,12 +109,11 @@ PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args) { PyObject *input; - fvec_t *vec; + fvec_t vec; PyObject *level_detection; smpl_t threshold; - if (!PyArg_ParseTuple (args, "Of:level_detection", &input, &threshold)) { - PyErr_SetString (PyExc_ValueError, "failed parsing arguments"); + if (!PyArg_ParseTuple (args, "O" AUBIO_NPY_SMPL_CHR ":level_detection", &input, &threshold)) { return NULL; } @@ -129,12 +121,11 @@ Py_aubio_level_detection(PyObject *self, PyObject *args) return NULL; } - vec = PyAubio_ArrayToCFvec (input); - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec(input, &vec)) { return NULL; } - level_detection = Py_BuildValue("f", aubio_level_detection(vec, threshold)); + level_detection = Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_level_detection(&vec, threshold)); if (level_detection == NULL) { PyErr_SetString (PyExc_ValueError, "failed computing level_detection"); return NULL; diff --git a/python/ext/py-musicutils.h b/python/ext/py-musicutils.h index f6d509f4..54ee2305 100644 --- a/python/ext/py-musicutils.h +++ b/python/ext/py-musicutils.h @@ -1,5 +1,5 @@ -#ifndef _PY_AUBIO_MUSICUTILS_H_ -#define _PY_AUBIO_MUSICUTILS_H_ +#ifndef PY_AUBIO_MUSICUTILS_H +#define PY_AUBIO_MUSICUTILS_H static char Py_aubio_window_doc[] = "" "window(string, integer) -> fvec\n" @@ -71,4 +71,4 @@ static char Py_aubio_level_detection_doc[] = "" PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args); -#endif /* _PY_AUBIO_MUSICUTILS_H_ */ +#endif /* PY_AUBIO_MUSICUTILS_H */ diff --git a/python/ext/py-phasevoc.c b/python/ext/py-phasevoc.c index 6767e921..74ddc180 100644 --- a/python/ext/py-phasevoc.c +++ b/python/ext/py-phasevoc.c @@ -1,10 +1,22 @@ -#include "aubiowraphell.h" +#include "aubio-types.h" static char Py_pvoc_doc[] = "pvoc object"; -AUBIO_DECLARE(pvoc, uint_t win_s; uint_t hop_s) +typedef struct +{ + PyObject_HEAD + aubio_pvoc_t * o; + uint_t win_s; + uint_t hop_s; + fvec_t vecin; + cvec_t cvecin; + PyObject *output; + cvec_t c_output; + PyObject *routput; + fvec_t c_routput; +} Py_pvoc; + -//AUBIO_NEW(pvoc) static PyObject * Py_pvoc_new (PyTypeObject * type, PyObject * args, PyObject * kwds) { @@ -49,64 +61,98 @@ Py_pvoc_new (PyTypeObject * type, PyObject * args, PyObject * kwds) return (PyObject *) self; } +static int +Py_pvoc_init (Py_pvoc * self, PyObject * args, PyObject * kwds) +{ + self->o = new_aubio_pvoc ( self->win_s, self->hop_s); + if (self->o == NULL) { + // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called + // AUBIO_ERR when failing + return -1; + } -AUBIO_INIT(pvoc, self->win_s, self->hop_s) + self->output = new_py_cvec(self->win_s); + self->routput = new_py_fvec(self->hop_s); + + return 0; +} -AUBIO_DEL(pvoc) -static PyObject * +static void +Py_pvoc_del (Py_pvoc *self, PyObject *unused) +{ + Py_XDECREF(self->output); + Py_XDECREF(self->routput); + if (self->o) { + del_aubio_pvoc(self->o); + } + Py_TYPE(self)->tp_free((PyObject *) self); +} + + +static PyObject * Py_pvoc_do(Py_pvoc * self, PyObject * args) { PyObject *input; - fvec_t *vec; - cvec_t *output; if (!PyArg_ParseTuple (args, "O", &input)) { return NULL; } - vec = PyAubio_ArrayToCFvec (input); - - if (vec == NULL) { + if (!PyAubio_ArrayToCFvec (input, &(self->vecin) )) { return NULL; } - output = new_cvec(self->win_s); + if (self->vecin.length != self->hop_s) { + PyErr_Format(PyExc_ValueError, + "input fvec has length %d, but pvoc expects length %d", + self->vecin.length, self->hop_s); + return NULL; + } + Py_INCREF(self->output); + if (!PyAubio_PyCvecToCCvec (self->output, &(self->c_output))) { + return NULL; + } // compute the function - aubio_pvoc_do (self->o, vec, output); - return (PyObject *)PyAubio_CCvecToPyCvec(output); + aubio_pvoc_do (self->o, &(self->vecin), &(self->c_output)); + return self->output; } -AUBIO_MEMBERS_START(pvoc) +static PyMemberDef Py_pvoc_members[] = { {"win_s", T_INT, offsetof (Py_pvoc, win_s), READONLY, "size of the window"}, {"hop_s", T_INT, offsetof (Py_pvoc, hop_s), READONLY, "size of the hop"}, -AUBIO_MEMBERS_STOP(pvoc) + { NULL } // sentinel +}; -static PyObject * +static PyObject * Py_pvoc_rdo(Py_pvoc * self, PyObject * args) { PyObject *input; - cvec_t *vec; - fvec_t *output; - if (!PyArg_ParseTuple (args, "O", &input)) { return NULL; } - vec = PyAubio_ArrayToCCvec (input); - - if (vec == NULL) { + if (!PyAubio_PyCvecToCCvec (input, &(self->cvecin) )) { return NULL; } - output = new_fvec(self->hop_s); + if (self->cvecin.length != self->win_s / 2 + 1) { + PyErr_Format(PyExc_ValueError, + "input cvec has length %d, but pvoc expects length %d", + self->cvecin.length, self->win_s / 2 + 1); + return NULL; + } + Py_INCREF(self->routput); + if (!PyAubio_ArrayToCFvec(self->routput, &(self->c_routput)) ) { + return NULL; + } // compute the function - aubio_pvoc_rdo (self->o, vec, output); - return (PyObject *)PyAubio_CFvecToArray(output); + aubio_pvoc_rdo (self->o, &(self->cvecin), &(self->c_routput)); + return self->routput; } static PyMethodDef Py_pvoc_methods[] = { @@ -115,4 +161,52 @@ static PyMethodDef Py_pvoc_methods[] = { {NULL} }; -AUBIO_TYPEOBJECT(pvoc, "aubio.pvoc") +PyTypeObject Py_pvocType = { + PyVarObject_HEAD_INIT (NULL, 0) + "aubio.pvoc", + sizeof (Py_pvoc), + 0, + (destructor) Py_pvoc_del, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (ternaryfunc)Py_pvoc_do, + 0, + 0, + 0, + 0, + Py_TPFLAGS_DEFAULT, + Py_pvoc_doc, + 0, + 0, + 0, + 0, + 0, + 0, + Py_pvoc_methods, + Py_pvoc_members, + 0, + 0, + 0, + 0, + 0, + 0, + (initproc) Py_pvoc_init, + 0, + Py_pvoc_new, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; diff --git a/python/ext/py-sink.c b/python/ext/py-sink.c index be5de364..3362abb7 100644 --- a/python/ext/py-sink.c +++ b/python/ext/py-sink.c @@ -1,4 +1,4 @@ -#include "aubiowraphell.h" +#include "aubio-types.h" typedef struct { @@ -7,6 +7,8 @@ typedef struct char_t* uri; uint_t samplerate; uint_t channels; + fvec_t write_data; + fmat_t mwrite_data; } Py_sink; static char Py_sink_doc[] = "" @@ -78,27 +80,20 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) return NULL; } - self->uri = "none"; + self->uri = NULL; if (uri != NULL) { - self->uri = uri; + self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1)); + strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1); } self->samplerate = Py_aubio_default_samplerate; - if ((sint_t)samplerate > 0) { + if (samplerate != 0) { self->samplerate = samplerate; - } else if ((sint_t)samplerate < 0) { - PyErr_SetString (PyExc_ValueError, - "can not use negative value for samplerate"); - return NULL; } self->channels = 1; - if ((sint_t)channels > 0) { + if (channels != 0) { self->channels = channels; - } else if ((sint_t)channels < 0) { - PyErr_SetString (PyExc_ValueError, - "can not use negative or null value for channels"); - return NULL; } return (PyObject *) self; @@ -107,24 +102,36 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) static int Py_sink_init (Py_sink * self, PyObject * args, PyObject * kwds) { - if (self->channels == 1) { - self->o = new_aubio_sink ( self->uri, self->samplerate ); - } else { - self->o = new_aubio_sink ( self->uri, 0 ); - aubio_sink_preset_channels ( self->o, self->channels ); - aubio_sink_preset_samplerate ( self->o, self->samplerate ); - } + self->o = new_aubio_sink ( self->uri, 0 ); if (self->o == NULL) { - PyErr_SetString (PyExc_StandardError, "error creating sink with this uri"); + // error string was set in new_aubio_sink + return -1; + } + if (aubio_sink_preset_channels(self->o, self->channels) != 0) { + // error string was set in aubio_sink_preset_channels + return -1; + } + if (aubio_sink_preset_samplerate(self->o, self->samplerate) != 0) { + // error string was set in aubio_sink_preset_samplerate return -1; } + self->samplerate = aubio_sink_get_samplerate ( self->o ); self->channels = aubio_sink_get_channels ( self->o ); return 0; } -AUBIO_DEL(sink) +static void +Py_sink_del (Py_sink *self, PyObject *unused) +{ + del_aubio_sink(self->o); + free(self->mwrite_data.data); + if (self->uri) { + free(self->uri); + } + Py_TYPE(self)->tp_free((PyObject *) self); +} /* function Py_sink_do */ static PyObject * @@ -134,7 +141,6 @@ Py_sink_do(Py_sink * self, PyObject * args) PyObject * write_data_obj; /* input vectors prototypes */ - fvec_t* write_data; uint_t write; @@ -142,20 +148,14 @@ Py_sink_do(Py_sink * self, PyObject * args) return NULL; } - /* input vectors parsing */ - write_data = PyAubio_ArrayToCFvec (write_data_obj); - - if (write_data == NULL) { + if (!PyAubio_ArrayToCFvec(write_data_obj, &(self->write_data))) { return NULL; } - - - /* compute _do function */ - aubio_sink_do (self->o, write_data, write); + aubio_sink_do (self->o, &(self->write_data), write); Py_RETURN_NONE; } @@ -168,7 +168,6 @@ Py_sink_do_multi(Py_sink * self, PyObject * args) PyObject * write_data_obj; /* input vectors prototypes */ - fmat_t * write_data; uint_t write; @@ -178,29 +177,24 @@ Py_sink_do_multi(Py_sink * self, PyObject * args) /* input vectors parsing */ - write_data = PyAubio_ArrayToCFmat (write_data_obj); - - if (write_data == NULL) { + if (!PyAubio_ArrayToCFmat(write_data_obj, &(self->mwrite_data))) { return NULL; } - - - - /* compute _do function */ - aubio_sink_do_multi (self->o, write_data, write); + aubio_sink_do_multi (self->o, &(self->mwrite_data), write); Py_RETURN_NONE; } -AUBIO_MEMBERS_START(sink) +static PyMemberDef Py_sink_members[] = { {"uri", T_STRING, offsetof (Py_sink, uri), READONLY, "path at which the sink was created"}, {"samplerate", T_INT, offsetof (Py_sink, samplerate), READONLY, "samplerate at which the sink was created"}, {"channels", T_INT, offsetof (Py_sink, channels), READONLY, "number of channels with which the sink was created"}, -AUBIO_MEMBERS_STOP(sink) + { NULL } // sentinel +}; static PyObject * Pyaubio_sink_close (Py_sink *self, PyObject *unused) @@ -209,11 +203,74 @@ Pyaubio_sink_close (Py_sink *self, PyObject *unused) Py_RETURN_NONE; } +static char Pyaubio_sink_enter_doc[] = ""; +static PyObject* Pyaubio_sink_enter(Py_sink *self, PyObject *unused) { + Py_INCREF(self); + return (PyObject*)self; +} + +static char Pyaubio_sink_exit_doc[] = ""; +static PyObject* Pyaubio_sink_exit(Py_sink *self, PyObject *unused) { + return Pyaubio_sink_close(self, unused); +} + static PyMethodDef Py_sink_methods[] = { {"do", (PyCFunction) Py_sink_do, METH_VARARGS, Py_sink_do_doc}, {"do_multi", (PyCFunction) Py_sink_do_multi, METH_VARARGS, Py_sink_do_multi_doc}, {"close", (PyCFunction) Pyaubio_sink_close, METH_NOARGS, Py_sink_close_doc}, + {"__enter__", (PyCFunction)Pyaubio_sink_enter, METH_NOARGS, + Pyaubio_sink_enter_doc}, + {"__exit__", (PyCFunction)Pyaubio_sink_exit, METH_VARARGS, + Pyaubio_sink_exit_doc}, {NULL} /* sentinel */ }; -AUBIO_TYPEOBJECT(sink, "aubio.sink") +PyTypeObject Py_sinkType = { + PyVarObject_HEAD_INIT (NULL, 0) + "aubio.sink", + sizeof (Py_sink), + 0, + (destructor) Py_sink_del, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (ternaryfunc)Py_sink_do, + 0, + 0, + 0, + 0, + Py_TPFLAGS_DEFAULT, + Py_sink_doc, + 0, + 0, + 0, + 0, + 0, + 0, + Py_sink_methods, + Py_sink_members, + 0, + 0, + 0, + 0, + 0, + 0, + (initproc) Py_sink_init, + 0, + Py_sink_new, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; diff --git a/python/ext/py-source.c b/python/ext/py-source.c index f9f972fb..219bdd42 100644 --- a/python/ext/py-source.c +++ b/python/ext/py-source.c @@ -1,4 +1,4 @@ -#include "aubiowraphell.h" +#include "aubio-types.h" typedef struct { @@ -8,6 +8,11 @@ typedef struct uint_t samplerate; uint_t channels; uint_t hop_size; + uint_t duration; + PyObject *read_to; + fvec_t c_read_to; + PyObject *mread_to; + fmat_t c_mread_to; } Py_source; static char Py_source_doc[] = "" @@ -95,9 +100,10 @@ Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) return NULL; } - self->uri = "none"; + self->uri = NULL; if (uri != NULL) { - self->uri = uri; + self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1)); + strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1); } self->samplerate = 0; @@ -135,48 +141,56 @@ Py_source_init (Py_source * self, PyObject * args, PyObject * kwds) { self->o = new_aubio_source ( self->uri, self->samplerate, self->hop_size ); if (self->o == NULL) { - char_t errstr[30 + strlen(self->uri)]; - sprintf(errstr, "error creating source with %s", self->uri); - PyErr_SetString (PyExc_StandardError, errstr); + // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called + // AUBIO_ERR when failing return -1; } self->samplerate = aubio_source_get_samplerate ( self->o ); if (self->channels == 0) { self->channels = aubio_source_get_channels ( self->o ); } + self->duration = aubio_source_get_duration ( self->o ); + + self->read_to = new_py_fvec(self->hop_size); + self->mread_to = new_py_fmat(self->channels, self->hop_size); return 0; } -AUBIO_DEL(source) +static void +Py_source_del (Py_source *self, PyObject *unused) +{ + if (self->o) { + del_aubio_source(self->o); + free(self->c_mread_to.data); + } + if (self->uri) { + free(self->uri); + } + Py_XDECREF(self->read_to); + Py_XDECREF(self->mread_to); + Py_TYPE(self)->tp_free((PyObject *) self); +} + /* function Py_source_do */ static PyObject * Py_source_do(Py_source * self, PyObject * args) { - - - /* output vectors prototypes */ - fvec_t* read_to; + PyObject *outputs; uint_t read; - - - - - - - /* creating output read_to as a new_fvec of length self->hop_size */ - read_to = new_fvec (self->hop_size); read = 0; - + Py_INCREF(self->read_to); + if (!PyAubio_ArrayToCFvec(self->read_to, &(self->c_read_to))) { + return NULL; + } /* compute _do function */ - aubio_source_do (self->o, read_to, &read); + aubio_source_do (self->o, &(self->c_read_to), &read); - PyObject *outputs = PyList_New(0); - PyList_Append( outputs, (PyObject *)PyAubio_CFvecToArray (read_to)); - //del_fvec (read_to); - PyList_Append( outputs, (PyObject *)PyInt_FromLong (read)); + outputs = PyTuple_New(2); + PyTuple_SetItem( outputs, 0, self->read_to ); + PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read)); return outputs; } @@ -184,33 +198,24 @@ Py_source_do(Py_source * self, PyObject * args) static PyObject * Py_source_do_multi(Py_source * self, PyObject * args) { - - - /* output vectors prototypes */ - fmat_t* read_to; + PyObject *outputs; uint_t read; - - - - - - - /* creating output read_to as a new_fvec of length self->hop_size */ - read_to = new_fmat (self->channels, self->hop_size); read = 0; - + Py_INCREF(self->mread_to); + if (!PyAubio_ArrayToCFmat(self->mread_to, &(self->c_mread_to))) { + return NULL; + } /* compute _do function */ - aubio_source_do_multi (self->o, read_to, &read); + aubio_source_do_multi (self->o, &(self->c_mread_to), &read); - PyObject *outputs = PyList_New(0); - PyList_Append( outputs, (PyObject *)PyAubio_CFmatToArray (read_to)); - //del_fvec (read_to); - PyList_Append( outputs, (PyObject *)PyInt_FromLong (read)); + outputs = PyTuple_New(2); + PyTuple_SetItem( outputs, 0, self->mread_to); + PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read)); return outputs; } -AUBIO_MEMBERS_START(source) +static PyMemberDef Py_source_members[] = { {"uri", T_STRING, offsetof (Py_source, uri), READONLY, "path at which the source was created"}, {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY, @@ -219,27 +224,29 @@ AUBIO_MEMBERS_START(source) "number of channels found in the source"}, {"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"}, -AUBIO_MEMBERS_STOP(source) - + {"duration", T_INT, offsetof (Py_source, duration), READONLY, + "total number of frames in the source (estimated)"}, + { NULL } // sentinel +}; static PyObject * Pyaubio_source_get_samplerate (Py_source *self, PyObject *unused) { uint_t tmp = aubio_source_get_samplerate (self->o); - return (PyObject *)PyInt_FromLong (tmp); + return (PyObject *)PyLong_FromLong (tmp); } static PyObject * Pyaubio_source_get_channels (Py_source *self, PyObject *unused) { uint_t tmp = aubio_source_get_channels (self->o); - return (PyObject *)PyInt_FromLong (tmp); + return (PyObject *)PyLong_FromLong (tmp); } static PyObject * Pyaubio_source_close (Py_source *self, PyObject *unused) { - aubio_source_close (self->o); + if (aubio_source_close(self->o) != 0) return NULL; Py_RETURN_NONE; } @@ -248,11 +255,18 @@ Pyaubio_source_seek (Py_source *self, PyObject *args) { uint_t err = 0; - uint_t position; + int position; if (!PyArg_ParseTuple (args, "I", &position)) { return NULL; } + if (position < 0) { + PyErr_Format(PyExc_ValueError, + "error when seeking in source: can not seek to negative value %d", + position); + return NULL; + } + err = aubio_source_seek(self->o, position); if (err != 0) { PyErr_SetString (PyExc_ValueError, @@ -262,6 +276,65 @@ Pyaubio_source_seek (Py_source *self, PyObject *args) Py_RETURN_NONE; } +static char Pyaubio_source_enter_doc[] = ""; +static PyObject* Pyaubio_source_enter(Py_source *self, PyObject *unused) { + Py_INCREF(self); + return (PyObject*)self; +} + +static char Pyaubio_source_exit_doc[] = ""; +static PyObject* Pyaubio_source_exit(Py_source *self, PyObject *unused) { + return Pyaubio_source_close(self, unused); +} + +static PyObject* Pyaubio_source_iter(PyObject *self) { + Py_INCREF(self); + return (PyObject*)self; +} + +static PyObject* Pyaubio_source_iter_next(Py_source *self) { + PyObject *done, *size; + if (self->channels == 1) { + done = Py_source_do(self, NULL); + } else { + done = Py_source_do_multi(self, NULL); + } + if (!PyTuple_Check(done)) { + PyErr_Format(PyExc_ValueError, + "error when reading source: not opened?"); + return NULL; + } + size = PyTuple_GetItem(done, 1); + if (size != NULL && PyLong_Check(size)) { + if (PyLong_AsLong(size) == (long)self->hop_size) { + PyObject *vec = PyTuple_GetItem(done, 0); + return vec; + } else if (PyLong_AsLong(size) > 0) { + // short read, return a shorter array + PyArrayObject *shortread = (PyArrayObject*)PyTuple_GetItem(done, 0); + PyArray_Dims newdims; + PyObject *reshaped; + newdims.len = PyArray_NDIM(shortread); + newdims.ptr = PyArray_DIMS(shortread); + // mono or multiple channels? + if (newdims.len == 1) { + newdims.ptr[0] = PyLong_AsLong(size); + } else { + newdims.ptr[1] = PyLong_AsLong(size); + } + reshaped = PyArray_Newshape(shortread, &newdims, NPY_CORDER); + Py_DECREF(shortread); + return reshaped; + } else { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + } else { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +} + static PyMethodDef Py_source_methods[] = { {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate, METH_NOARGS, Py_source_get_samplerate_doc}, @@ -275,7 +348,59 @@ static PyMethodDef Py_source_methods[] = { METH_NOARGS, Py_source_close_doc}, {"seek", (PyCFunction) Pyaubio_source_seek, METH_VARARGS, Py_source_seek_doc}, + {"__enter__", (PyCFunction)Pyaubio_source_enter, METH_NOARGS, + Pyaubio_source_enter_doc}, + {"__exit__", (PyCFunction)Pyaubio_source_exit, METH_VARARGS, + Pyaubio_source_exit_doc}, {NULL} /* sentinel */ }; -AUBIO_TYPEOBJECT(source, "aubio.source") +PyTypeObject Py_sourceType = { + PyVarObject_HEAD_INIT (NULL, 0) + "aubio.source", + sizeof (Py_source), + 0, + (destructor) Py_source_del, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (ternaryfunc)Py_source_do, + 0, + 0, + 0, + 0, + Py_TPFLAGS_DEFAULT, + Py_source_doc, + 0, + 0, + 0, + 0, + Pyaubio_source_iter, + (unaryfunc)Pyaubio_source_iter_next, + Py_source_methods, + Py_source_members, + 0, + 0, + 0, + 0, + 0, + 0, + (initproc) Py_source_init, + 0, + Py_source_new, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; diff --git a/python/ext/ufuncs.c b/python/ext/ufuncs.c index 455247b9..8a4e9172 100644 --- a/python/ext/ufuncs.c +++ b/python/ext/ufuncs.c @@ -84,6 +84,7 @@ static void* Py_miditofreq_data[] = { void add_ufuncs ( PyObject *m ) { int err = 0; + PyObject *dict, *f, *g, *h; err = _import_umath (); if (err != 0) { @@ -91,7 +92,6 @@ void add_ufuncs ( PyObject *m ) "Unable to import Numpy umath from aubio module (error %d)\n", err); } - PyObject *f, *dict; dict = PyModule_GetDict(m); f = PyUFunc_FromFuncAndData(Py_aubio_unary_functions, Py_unwrap2pi_data, Py_aubio_unary_types, Py_aubio_unary_n_types, Py_aubio_unary_n_inputs, Py_aubio_unary_n_outputs, @@ -99,14 +99,12 @@ void add_ufuncs ( PyObject *m ) PyDict_SetItemString(dict, "unwrap2pi", f); Py_DECREF(f); - PyObject *g; g = PyUFunc_FromFuncAndData(Py_aubio_unary_functions, Py_freqtomidi_data, Py_aubio_unary_types, Py_aubio_unary_n_types, Py_aubio_unary_n_inputs, Py_aubio_unary_n_outputs, PyUFunc_None, "freqtomidi", Py_freqtomidi_doc, 0); PyDict_SetItemString(dict, "freqtomidi", g); Py_DECREF(g); - PyObject *h; h = PyUFunc_FromFuncAndData(Py_aubio_unary_functions, Py_miditofreq_data, Py_aubio_unary_types, Py_aubio_unary_n_types, Py_aubio_unary_n_inputs, Py_aubio_unary_n_outputs, PyUFunc_None, "miditofreq", Py_miditofreq_doc, 0); diff --git a/python/lib/aubio/__init__.py b/python/lib/aubio/__init__.py index 50d4a171..e7b891c4 100644 --- a/python/lib/aubio/__init__.py +++ b/python/lib/aubio/__init__.py @@ -1,14 +1,19 @@ #! /usr/bin/env python import numpy -from _aubio import * -from midiconv import * -from slicing import * +from ._aubio import __version__ as version +from ._aubio import float_type +from ._aubio import * +from .midiconv import * +from .slicing import * class fvec(numpy.ndarray): - " a simple numpy array holding a vector of float32 " - def __new__(self, length = 1024, **kwargs): - self.length = length - if type(length) == type([]): - return numpy.array(length, dtype='float32', **kwargs) - return numpy.zeros(length, dtype='float32', **kwargs) + """a numpy vector holding audio samples""" + + def __new__(cls, input_arg=1024, **kwargs): + 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) + else: + return numpy.array(input_arg, dtype=float_type, **kwargs) diff --git a/python/lib/aubio/midiconv.py b/python/lib/aubio/midiconv.py index a859dec1..c79523f5 100644 --- a/python/lib/aubio/midiconv.py +++ b/python/lib/aubio/midiconv.py @@ -1,14 +1,33 @@ # -*- coding: utf-8 -*- +""" utilities to convert midi note number to and from note names """ + +__all__ = ['note2midi', 'midi2note', 'freq2note'] + +import sys +py3 = sys.version_info[0] == 3 +if py3: + str_instances = str + int_instances = int +else: + 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} - _valid_modifiers = {None: 0, u'♮': 0, '#': +1, u'♯': +1, u'\udd2a': +2, 'b': -1, u'♭': -1, u'\ufffd': -2} + _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 + } _valid_octaves = range(-1, 10) - if type(note) not in (str, unicode): - raise TypeError, "a string is required, got %s" % note - if not (1 < len(note) < 5): - raise ValueError, "string of 2 to 4 characters expected, got %d (%s)" % (len(note), note) + if not isinstance(note, str_instances): + raise TypeError("a string is required, got %s (%s)" % (note, str(type(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 if len(note) == 4: @@ -26,27 +45,27 @@ def note2midi(note): octave = int(octave) if notename not in _valid_notenames: - raise ValueError, "%s is not a valid note name" % notename + raise ValueError("%s is not a valid note name" % notename) if modifier not in _valid_modifiers: - raise ValueError, "%s is not a valid modifier" % modifier + raise ValueError("%s is not a valid modifier" % modifier) if octave not in _valid_octaves: - raise ValueError, "%s is not a valid octave" % octave + raise ValueError("%s is not a valid octave" % octave) midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier] if midi > 127: - raise ValueError, "%s is outside of the range C-2 to G8" % note + 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] " - if type(midi) != int: - raise TypeError, "an integer is required, got %s" % midi - if not (-1 < midi < 128): - raise ValueError, "an integer between 0 and 127 is excepted, got %d" % midi - midi = int(midi) + 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'] - return _valid_notenames[midi % 12] + str( midi / 12 - 1) + 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))) diff --git a/python/lib/aubio/slicing.py b/python/lib/aubio/slicing.py index 52d36ea8..fa9d2e34 100644 --- a/python/lib/aubio/slicing.py +++ b/python/lib/aubio/slicing.py @@ -1,50 +1,52 @@ -from aubio import source, sink +"""utility routines to slice sound files at given timestamps""" + import os +from aubio import source, sink -max_timestamp = 1e120 +_max_timestamp = 1e120 -def slice_source_at_stamps(source_file, timestamps, timestamps_end = None, - output_dir = None, - samplerate = 0, - hopsize = 256): +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 """ - if timestamps == None or len(timestamps) == 0: - raise ValueError ("no timestamps given") + if timestamps is None or len(timestamps) == 0: + raise ValueError("no timestamps given") if timestamps[0] != 0: timestamps = [0] + timestamps - if timestamps_end != None: + if timestamps_end is not None: timestamps_end = [timestamps[1] - 1] + timestamps_end - if timestamps_end != None: + if timestamps_end is not None: if len(timestamps_end) != len(timestamps): - raise ValueError ("len(timestamps_end) != len(timestamps)") + raise ValueError("len(timestamps_end) != len(timestamps)") else: - timestamps_end = [t - 1 for t in timestamps[1:] ] + [ max_timestamp ] + timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp] - regions = zip(timestamps, timestamps_end) + regions = list(zip(timestamps, timestamps_end)) #print regions - source_base_name, source_ext = os.path.splitext(os.path.basename(source_file)) - if output_dir != None: + source_base_name, _ = os.path.splitext(os.path.basename(source_file)) + if output_dir is not None: if not os.path.isdir(output_dir): 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 """ timestamp_seconds = timestamp / float(samplerate) return source_base_name + "_%011.6f" % timestamp_seconds + '.wav' - # reopen source file - s = source(source_file, samplerate, hopsize) - samplerate = s.get_samplerate() + # open source file + _source = source(source_file, samplerate, hopsize) + samplerate = _source.samplerate total_frames = 0 slices = [] while True: # get hopsize new samples from source - vec, read = s.do_multi() + 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 @@ -53,16 +55,16 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end = None, # create a name for the sink new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate) # create its sink - g = sink(new_sink_path, samplerate, s.channels) + _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': g} + 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) for current_slice in slices: start_stamp = current_slice['start_stamp'] end_stamp = current_slice['end_stamp'] - g = current_slice['sink'] + _sink = current_slice['sink'] # sample index to start writing from new source vector start = max(start_stamp - total_frames, 0) # number of samples yet to written be until end of region @@ -72,12 +74,13 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end = None, if remaining < read: if remaining > start: # write remaining samples from current region - g.do_multi(vec[:,start:remaining], remaining - start) + _sink.do_multi(vec[:, start:remaining], remaining - start) #print "closing region", "remaining", remaining # close this file - g.close() + _sink.close() elif read > start: # write all the samples - g.do_multi(vec[:,start:read], read - start) + _sink.do_multi(vec[:, start:read], read - start) total_frames += read - if read < hopsize: break + if read < hopsize: + break diff --git a/python/lib/gen_code.py b/python/lib/gen_code.py new file mode 100644 index 00000000..c075cc5e --- /dev/null +++ b/python/lib/gen_code.py @@ -0,0 +1,573 @@ +aubiodefvalue = { + # we have some clean up to do + 'buf_size': 'Py_default_vector_length', + 'win_s': 'Py_default_vector_length', + # and here too + 'hop_size': 'Py_default_vector_length / 2', + 'hop_s': 'Py_default_vector_length / 2', + # these should be alright + 'samplerate': 'Py_aubio_default_samplerate', + # now for the non obvious ones + 'n_filters': '40', + 'n_coeffs': '13', + 'nelems': '10', + 'flow': '0.', + 'fhig': '1.', + 'ilow': '0.', + 'ihig': '1.', + 'thrs': '0.5', + 'ratio': '0.5', + 'method': '"default"', + 'uri': '"none"', + } + +member_types = { + 'name': 'type', + 'char_t*': 'T_STRING', + 'uint_t': 'T_INT', + 'smpl_t': 'AUBIO_NPY_SMPL', + } + +pyfromtype_fn = { + 'smpl_t': 'PyFloat_FromDouble', + 'uint_t': 'PyLong_FromLong', # was: 'PyInt_FromLong', + 'fvec_t*': 'PyAubio_CFvecToArray', + 'fmat_t*': 'PyAubio_CFmatToArray', + } + +pytoaubio_fn = { + 'fvec_t*': 'PyAubio_ArrayToCFvec', + 'cvec_t*': 'PyAubio_PyCvecToCCvec', + #'fmat_t*': 'PyAubio_ArrayToCFmat', + } + +newfromtype_fn = { + 'fvec_t*': 'new_py_fvec', + 'fmat_t*': 'new_py_fmat', + 'cvec_t*': 'new_py_cvec', + } + +delfromtype_fn = { + 'fvec_t*': 'Py_DECREF', + 'fmat_t*': 'Py_DECREF', + 'cvec_t*': 'Py_DECREF', + } + +param_init = { + 'char_t*': 'NULL', + 'uint_t': '0', + 'sint_t': 0, + 'smpl_t': 0., + 'lsmp_t': 0., + } + +pyargparse_chars = { + 'smpl_t': 'f', # if not usedouble else 'd', + 'uint_t': 'I', + 'sint_t': 'I', + 'char_t*': 's', + 'fmat_t*': 'O', + 'fvec_t*': 'O', + 'cvec_t*': 'O', + } + +objoutsize = { + 'onset': '1', + 'pitch': '1', + 'notes': '3', + 'wavetable': 'self->hop_size', + 'sampler': 'self->hop_size', + 'mfcc': 'self->n_coeffs', + 'specdesc': '1', + 'tempo': '1', + 'filterbank': 'self->n_filters', + 'tss': 'self->buf_size', + } + +objinputsize = { + 'mfcc': 'self->buf_size / 2 + 1', + 'notes': 'self->hop_size', + 'onset': 'self->hop_size', + 'pitch': 'self->hop_size', + 'sampler': 'self->hop_size', + 'specdesc': 'self->buf_size / 2 + 1', + 'tempo': 'self->hop_size', + 'wavetable': 'self->hop_size', + 'tss': 'self->buf_size / 2 + 1', + } + +def get_name(proto): + name = proto.replace(' *', '* ').split()[1].split('(')[0] + name = name.replace('*','') + if name == '': raise ValueError(proto + "gave empty name") + return name + +def get_return_type(proto): + import re + paramregex = re.compile('(\w+ ?\*?).*') + outputs = paramregex.findall(proto) + assert len(outputs) == 1 + return outputs[0].replace(' ', '') + +def split_type(arg): + """ arg = 'foo *name' + return ['foo*', 'name'] """ + l = arg.split() + type_arg = {} #'type': l[0], 'name': l[1]} + type_arg['type'] = " ".join(l[:-1]) + type_arg['name'] = l[-1] + # fix up type / name + if type_arg['name'].startswith('*'): + # ['foo', '*name'] -> ['foo*', 'name'] + type_arg['type'] += '*' + type_arg['name'] = type_arg['name'][1:] + if type_arg['type'].endswith(' *'): + # ['foo *', 'name'] -> ['foo*', 'name'] + type_arg['type'] = type_arg['type'].replace(' *','*') + if type_arg['type'].startswith('const '): + # ['foo *', 'name'] -> ['foo*', 'name'] + type_arg['type'] = type_arg['type'].replace('const ','') + return type_arg + +def get_params(proto): + """ get the list of parameters from a function prototype + example: proto = "int main (int argc, char ** argv)" + returns: ['int argc', 'char ** argv'] + """ + import re + paramregex = re.compile('.*\((.*)\);') + a = paramregex.findall(proto)[0].split(', ') + #a = [i.replace('const ', '') for i in a] + return a + +def get_input_params(proto): + a = get_params(proto) + return [i.replace('const ', '') for i in a if (i.startswith('const ') or i.startswith('uint_t ') or i.startswith('smpl_t '))] + +def get_output_params(proto): + a = get_params(proto) + return [i for i in a if not i.startswith('const ')][1:] + +def get_params_types_names(proto): + """ get the list of parameters from a function prototype + example: proto = "int main (int argc, char ** argv)" + returns: [['int', 'argc'], ['char **','argv']] + """ + a = list(map(split_type, get_params(proto))) + #print proto, a + #import sys; sys.exit(1) + return a + +class MappedObject(object): + + def __init__(self, prototypes, usedouble = False): + if usedouble: + pyargparse_chars['smpl_t'] = 'd' + self.prototypes = prototypes + + self.shortname = prototypes['shortname'] + self.longname = prototypes['longname'] + self.new_proto = prototypes['new'][0] + self.del_proto = prototypes['del'][0] + self.do_proto = prototypes['do'][0] + self.input_params = get_params_types_names(self.new_proto) + self.input_params_list = "; ".join(get_input_params(self.new_proto)) + self.outputs = get_params_types_names(self.do_proto)[2:] + 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] + self.struct_outputs = ";\n ".join(struct_output_str) + + #print ("input_params: ", map(split_type, get_input_params(self.do_proto))) + #print ("output_params", map(split_type, get_output_params(self.do_proto))) + + def gen_code(self): + out = "" + out += self.gen_struct() + out += self.gen_doc() + out += self.gen_new() + out += self.gen_init() + out += self.gen_del() + out += self.gen_do() + out += self.gen_memberdef() + out += self.gen_set() + out += self.gen_get() + out += self.gen_methodef() + out += self.gen_typeobject() + return out + + def gen_struct(self): + out = """ +// {shortname} structure +typedef struct{{ + PyObject_HEAD + // pointer to aubio object + {longname} *o; + // input parameters + {input_params_list}; + // do input vectors + {do_inputs_list}; + // output results + {struct_outputs}; +}} Py_{shortname}; +""" + # fmat_t* / fvec_t* / cvec_t* inputs -> full fvec_t /.. struct in Py_{shortname} + do_inputs_list = "; ".join(get_input_params(self.do_proto)).replace('fvec_t *','fvec_t').replace('fmat_t *', 'fmat_t').replace('cvec_t *', 'cvec_t') + return out.format(do_inputs_list = do_inputs_list, **self.__dict__) + + def gen_doc(self): + out = """ +// TODO: add documentation +static char Py_{shortname}_doc[] = \"undefined\"; +""" + return out.format(**self.__dict__) + + def gen_new(self): + out = """ +// new {shortname} +static PyObject * +Py_{shortname}_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) +{{ + Py_{shortname} *self; +""".format(**self.__dict__) + params = self.input_params + for p in params: + out += """ + {type} {name} = {defval};""".format(defval = param_init[p['type']], **p) + plist = ", ".join(["\"%s\"" % p['name'] for p in params]) + out += """ + static char *kwlist[] = {{ {plist}, NULL }};""".format(plist = plist) + argchars = "".join([pyargparse_chars[p['type']] for p in params]) + arglist = ", ".join(["&%s" % p['name'] for p in params]) + out += """ + if (!PyArg_ParseTupleAndKeywords (args, kwds, "|{argchars}", kwlist, + {arglist})) {{ + return NULL; + }} +""".format(argchars = argchars, arglist = arglist) + out += """ + self = (Py_{shortname} *) pytype->tp_alloc (pytype, 0); + if (self == NULL) {{ + return NULL; + }} +""".format(**self.__dict__) + params = self.input_params + for p in params: + out += self.check_valid(p) + out += """ + return (PyObject *)self; +} +""" + return out + + def check_valid(self, p): + if p['type'] == 'uint_t': + return self.check_valid_uint(p) + if p['type'] == 'char_t*': + return self.check_valid_char(p) + else: + print ("ERROR, no idea how to check %s for validity" % p['type']) + + def check_valid_uint(self, p): + name = p['name'] + return """ + self->{name} = {defval}; + if ((sint_t){name} > 0) {{ + self->{name} = {name}; + }} else if ((sint_t){name} < 0) {{ + PyErr_SetString (PyExc_ValueError, "can not use negative value for {name}"); + return NULL; + }} +""".format(defval = aubiodefvalue[name], name = name) + + def check_valid_char(self, p): + name = p['name'] + return """ + self->{name} = {defval}; + if ({name} != NULL) {{ + self->{name} = {name}; + }} +""".format(defval = aubiodefvalue[name], name = name) + + def gen_init(self): + out = """ +// init {shortname} +static int +Py_{shortname}_init (Py_{shortname} * self, PyObject * args, PyObject * kwds) +{{ +""".format(**self.__dict__) + new_name = get_name(self.new_proto) + new_params = ", ".join(["self->%s" % s['name'] for s in self.input_params]) + out += """ + self->o = {new_name}({new_params}); +""".format(new_name = new_name, new_params = new_params) + paramchars = "%s" + paramvals = "self->method" + out += """ + // return -1 and set error string on failure + if (self->o == NULL) {{ + PyErr_Format (PyExc_RuntimeError, "failed creating {shortname}"); + return -1; + }} +""".format(paramchars = paramchars, paramvals = paramvals, **self.__dict__) + output_create = "" + for o in self.outputs: + output_create += """ + self->{name} = {create_fn}({output_size});""".format(name = o['name'], create_fn = newfromtype_fn[o['type']], output_size = objoutsize[self.shortname]) + out += """ + // TODO get internal params after actual object creation? +""" + out += """ + // create outputs{output_create} +""".format(output_create = output_create) + out += """ + return 0; +} +""" + return out + + def gen_memberdef(self): + out = """ +static PyMemberDef Py_{shortname}_members[] = {{ +""".format(**self.__dict__) + for p in get_params_types_names(self.new_proto): + tmp = " {{\"{name}\", {ttype}, offsetof (Py_{shortname}, {name}), READONLY, \"TODO documentation\"}},\n" + pytype = member_types[p['type']] + out += tmp.format(name = p['name'], ttype = pytype, shortname = self.shortname) + out += """ {NULL}, // sentinel +}; +""" + return out + + def gen_del(self): + out = """ +// del {shortname} +static void +Py_{shortname}_del (Py_{shortname} * self, PyObject * unused) +{{""".format(**self.__dict__) + for input_param in self.do_inputs: + if input_param['type'] == 'fmat_t *': + out += """ + free(self->{0[name]}.data);""".format(input_param) + for o in self.outputs: + name = o['name'] + del_out = delfromtype_fn[o['type']] + out += """ + if (self->{name}) {{ + {del_out}(self->{name}); + }}""".format(del_out = del_out, name = name) + del_fn = get_name(self.del_proto) + out += """ + if (self->o) {{ + {del_fn}(self->o); + }} + Py_TYPE(self)->tp_free((PyObject *) self); +}} +""".format(del_fn = del_fn) + return out + + def gen_do(self): + out = """ +// do {shortname} +static PyObject* +Py_{shortname}_do (Py_{shortname} * self, PyObject * args) +{{""".format(**self.__dict__) + input_params = self.do_inputs + output_params = self.do_outputs + #print input_params + #print output_params + out += """ + PyObject *outputs;""" + for input_param in input_params: + out += """ + PyObject *py_{0};""".format(input_param['name']) + refs = ", ".join(["&py_%s" % p['name'] for p in input_params]) + pyparamtypes = "".join([pyargparse_chars[p['type']] for p in input_params]) + out += """ + if (!PyArg_ParseTuple (args, "{pyparamtypes}", {refs})) {{ + return NULL; + }}""".format(refs = refs, pyparamtypes = pyparamtypes, **self.__dict__) + for input_param in input_params: + out += """ + + if (!{pytoaubio}(py_{0[name]}, &(self->{0[name]}))) {{ + return NULL; + }}""".format(input_param, pytoaubio = pytoaubio_fn[input_param['type']]) + if self.shortname in objinputsize: + out += """ + + if (self->{0[name]}.length != {expected_size}) {{ + PyErr_Format (PyExc_ValueError, + "input size of {shortname} should be %d, not %d", + {expected_size}, self->{0[name]}.length); + return NULL; + }}""".format(input_param, expected_size = objinputsize[self.shortname], **self.__dict__) + else: + out += """ + + // TODO: check input sizes""" + for output_param in output_params: + out += """ + + Py_INCREF(self->{0[name]}); + if (!{pytoaubio}(self->{0[name]}, &(self->c_{0[name]}))) {{ + return NULL; + }}""".format(output_param, pytoaubio = pytoaubio_fn[output_param['type']]) + do_fn = get_name(self.do_proto) + inputs = ", ".join(['&(self->'+p['name']+')' for p in input_params]) + c_outputs = ", ".join(["&(self->c_%s)" % p['name'] for p in self.do_outputs]) + outputs = ", ".join(["self->%s" % p['name'] for p in self.do_outputs]) + out += """ + + {do_fn}(self->o, {inputs}, {c_outputs}); +""".format( + do_fn = do_fn, + inputs = inputs, c_outputs = c_outputs, + ) + if len(self.do_outputs) > 1: + out += """ + outputs = PyTuple_New({:d});""".format(len(self.do_outputs)) + for i, p in enumerate(self.do_outputs): + out += """ + PyTuple_SetItem( outputs, {i}, self->{p[name]});""".format(i = i, p = p) + else: + out += """ + outputs = self->{p[name]};""".format(p = self.do_outputs[0]) + out += """ + + return outputs; +}} +""".format( + outputs = outputs, + ) + return out + + def gen_set(self): + out = """ +// {shortname} setters +""".format(**self.__dict__) + for set_param in self.prototypes['set']: + params = get_params_types_names(set_param)[1] + paramtype = params['type'] + method_name = get_name(set_param) + param = method_name.split('aubio_'+self.shortname+'_set_')[-1] + pyparamtype = pyargparse_chars[paramtype] + out += """ +static PyObject * +Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args) +{{ + uint_t err = 0; + {paramtype} {param}; + + if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{ + return NULL; + }} + err = aubio_{shortname}_set_{param} (self->o, {param}); + + if (err > 0) {{ + PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}"); + return NULL; + }} + Py_RETURN_NONE; +}} +""".format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__) + return out + + def gen_get(self): + out = """ +// {shortname} getters +""".format(**self.__dict__) + for method in self.prototypes['get']: + params = get_params_types_names(method) + method_name = get_name(method) + assert len(params) == 1, \ + "get method has more than one parameter %s" % params + param = method_name.split('aubio_'+self.shortname+'_get_')[-1] + paramtype = get_return_type(method) + ptypeconv = pyfromtype_fn[paramtype] + out += """ +static PyObject * +Pyaubio_{shortname}_get_{param} (Py_{shortname} *self, PyObject *unused) +{{ + {ptype} {param} = aubio_{shortname}_get_{param} (self->o); + return (PyObject *){ptypeconv} ({param}); +}} +""".format(param = param, ptype = paramtype, ptypeconv = ptypeconv, + **self.__dict__) + return out + + def gen_methodef(self): + out = """ +static PyMethodDef Py_{shortname}_methods[] = {{""".format(**self.__dict__) + for m in self.prototypes['set']: + name = get_name(m) + shortname = name.replace('aubio_%s_' % self.shortname, '') + out += """ + {{"{shortname}", (PyCFunction) Py{name}, + METH_VARARGS, ""}},""".format(name = name, shortname = shortname) + for m in self.prototypes['get']: + name = get_name(m) + shortname = name.replace('aubio_%s_' % self.shortname, '') + out += """ + {{"{shortname}", (PyCFunction) Py{name}, + METH_NOARGS, ""}},""".format(name = name, shortname = shortname) + out += """ + {NULL} /* sentinel */ +}; +""" + return out + + def gen_typeobject(self): + return """ +PyTypeObject Py_{shortname}Type = {{ + //PyObject_HEAD_INIT (NULL) + //0, + PyVarObject_HEAD_INIT (NULL, 0) + "aubio.{shortname}", + sizeof (Py_{shortname}), + 0, + (destructor) Py_{shortname}_del, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (ternaryfunc)Py_{shortname}_do, + 0, + 0, + 0, + 0, + Py_TPFLAGS_DEFAULT, + Py_{shortname}_doc, + 0, + 0, + 0, + 0, + 0, + 0, + Py_{shortname}_methods, + Py_{shortname}_members, + 0, + 0, + 0, + 0, + 0, + 0, + (initproc) Py_{shortname}_init, + 0, + Py_{shortname}_new, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}}; +""".format(**self.__dict__) diff --git a/python/lib/gen_external.py b/python/lib/gen_external.py new file mode 100644 index 00000000..d46c1f59 --- /dev/null +++ b/python/lib/gen_external.py @@ -0,0 +1,256 @@ +import distutils.ccompiler +import sys, os, subprocess, glob + +header = os.path.join('src', 'aubio.h') +output_path = os.path.join('python', 'gen') + +source_header = """// this file is generated! do not modify +#include "aubio-types.h" +""" + +skip_objects = [ + # already in ext/ + 'fft', + 'pvoc', + 'filter', + 'filterbank', + # AUBIO_UNSTABLE + 'hist', + 'parameter', + 'scale', + 'beattracking', + 'resampler', + 'peakpicker', + 'pitchfcomb', + 'pitchmcomb', + 'pitchschmitt', + 'pitchspecacf', + 'pitchyin', + 'pitchyinfft', + 'sink', + 'sink_apple_audio', + 'sink_sndfile', + 'sink_wavwrite', + #'mfcc', + 'source', + 'source_apple_audio', + 'source_sndfile', + 'source_avcodec', + 'source_wavread', + #'sampler', + 'audio_unit', + ] + +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: + customize_compiler(compiler) + except AttributeError as e: + print("Warning: failed customizing compiler ({:s})".format(repr(e))) + + if hasattr(compiler, 'initialize'): + try: + compiler.initialize() + except ValueError as e: + print("Warning: failed initializing compiler ({:s})".format(repr(e))) + + cpp_cmd = None + if hasattr(compiler, 'preprocessor'): # for unixccompiler + cpp_cmd = compiler.preprocessor + elif hasattr(compiler, 'compiler'): # for ccompiler + cpp_cmd = compiler.compiler.split() + cpp_cmd += ['-E'] + elif hasattr(compiler, 'cc'): # for msvccompiler + cpp_cmd = compiler.cc.split() + cpp_cmd += ['-E'] + + 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'] + + return cpp_cmd + +def get_cpp_objects(header=header, usedouble=False): + cpp_cmd = get_preprocessor() + + macros = [('AUBIO_UNSTABLE', 1)] + if usedouble: + macros += [('HAVE_AUBIO_DOUBLE', 1)] + + if not os.path.isfile(header): + raise Exception("could not find include file " + header) + + includes = [os.path.dirname(header)] + cpp_cmd += distutils.ccompiler.gen_preprocess_options(macros, includes) + cpp_cmd += [header] + + print("Running command: {:s}".format(" ".join(cpp_cmd))) + proc = subprocess.Popen(cpp_cmd, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + assert proc, 'Proc was none' + cpp_output = proc.stdout.read() + err_output = proc.stderr.read() + 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) + if not isinstance(cpp_output, list): + cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')] + + cpp_output = filter(lambda y: len(y) > 1, cpp_output) + cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output)) + + i = 1 + while 1: + if i >= len(cpp_output): break + if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'): + cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i] + cpp_output.pop(i-1) + else: + i += 1 + + typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output) + + cpp_objects = [a.split()[3][:-1] for a in typedefs] + + return cpp_output, cpp_objects + + +def analyze_cpp_output(cpp_objects, cpp_output): + lib = {} + + for o in cpp_objects: + if o[:6] != 'aubio_': + continue + shortname = o[6:-2] + if shortname in skip_objects: + continue + lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []} + lib[shortname]['longname'] = o + lib[shortname]['shortname'] = shortname + for fn in cpp_output: + if o[:-1] in fn: + #print "found", o[:-1], "in", fn + if 'typedef struct ' in fn: + lib[shortname]['struct'].append(fn) + elif '_do' in fn: + lib[shortname]['do'].append(fn) + elif 'new_' in fn: + lib[shortname]['new'].append(fn) + elif 'del_' in fn: + lib[shortname]['del'].append(fn) + elif '_get_' in fn: + lib[shortname]['get'].append(fn) + elif '_set_' in fn: + lib[shortname]['set'].append(fn) + else: + #print "no idea what to do about", fn + lib[shortname]['other'].append(fn) + return lib + +def print_cpp_output_results(lib, cpp_output): + for fn in cpp_output: + found = 0 + for o in lib: + for family in lib[o]: + if fn in lib[o][family]: + found = 1 + if found == 0: + print ("missing", fn) + + for o in lib: + for family in lib[o]: + if type(lib[o][family]) == str: + print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) ) + elif len(lib[o][family]) == 1: + print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ) ) + else: + print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) ) + + +def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True): + if not os.path.isdir(output_path): os.mkdir(output_path) + elif not overwrite: return sorted(glob.glob(os.path.join(output_path, '*.c'))) + + cpp_output, cpp_objects = get_cpp_objects(header, usedouble=usedouble) + + lib = analyze_cpp_output(cpp_objects, cpp_output) + # print_cpp_output_results(lib, cpp_output) + + 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) + out += mapped.gen_code() + output_file = os.path.join(output_path, 'gen-%s.c' % o) + with open(output_file, 'w') as f: + f.write(out) + print ("wrote %s" % output_file ) + sources_list.append(output_file) + + out = source_header + out += "#include \"aubio-generated.h\"" + check_types = "\n || ".join(["PyType_Ready(&Py_%sType) < 0" % o for o in lib]) + out += """ + +int generated_types_ready (void) +{{ + return ({pycheck_types}); +}} +""".format(pycheck_types = check_types) + + add_types = "".join([""" + Py_INCREF (&Py_{name}Type); + PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name = o) for o in lib]) + out += """ + +void add_generated_objects ( PyObject *m ) +{{ +{add_types} +}} +""".format(add_types = add_types) + + output_file = os.path.join(output_path, 'aubio-generated.c') + with open(output_file, 'w') as f: + f.write(out) + print ("wrote %s" % output_file ) + sources_list.append(output_file) + + objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib]) + out = """// generated list of objects created with gen_external.py + +#include +""" + if usedouble: + out += """ +#ifndef HAVE_AUBIO_DOUBLE +#define HAVE_AUBIO_DOUBLE 1 +#endif +""" + out += """ +{objlist} +int generated_objects ( void ); +void add_generated_objects( PyObject *m ); +""".format(objlist = objlist) + + output_file = os.path.join(output_path, 'aubio-generated.h') + with open(output_file, 'w') as f: + f.write(out) + print ("wrote %s" % output_file ) + # no need to add header to list of sources + + return sorted(sources_list) + +if __name__ == '__main__': + if len(sys.argv) > 1: header = sys.argv[1] + if len(sys.argv) > 2: output_path = sys.argv[2] + generate_external(header, output_path) diff --git a/python/lib/gen_pyobject.py b/python/lib/gen_pyobject.py deleted file mode 100644 index ba8274e5..00000000 --- a/python/lib/gen_pyobject.py +++ /dev/null @@ -1,529 +0,0 @@ -#! /usr/bin/python - -""" This madness of code is used to generate the C code of the python interface -to aubio. Don't try this at home. - -The list of typedefs and functions is obtained from the command line 'cpp -aubio.h'. This list is then used to parse all the functions about this object. - -I hear the ones asking "why not use swig, or cython, or something like that?" - -The requirements for this extension are the following: - - - aubio vectors can be viewed as numpy arrays, and vice versa - - aubio 'object' should be python classes, not just a bunch of functions - -I haven't met any python interface generator that can meet both these -requirements. If you know of one, please let me know, it will spare me -maintaining this bizarre file. -""" - -param_numbers = { - 'source': [0, 2], - 'sink': [2, 0], - 'sampler': [1, 1], -} - -# TODO -# do function: for now, only the following pattern is supported: -# void aubio__do (aubio_foo_t * o, -# [input1_t * input, [output1_t * output, ..., output3_t * output]]); -# There is no way of knowing that output1 is actually input2. In the future, -# const could be used for the inputs in the C prototypes. - -def write_msg(*args): - pass - # uncomment out for debugging - #print args - -def split_type(arg): - """ arg = 'foo *name' - return ['foo*', 'name'] """ - l = arg.split() - type_arg = {'type': l[0], 'name': l[1]} - # ['foo', '*name'] -> ['foo*', 'name'] - if l[-1].startswith('*'): - #return [l[0]+'*', l[1][1:]] - type_arg['type'] = l[0] + '*' - type_arg['name'] = l[1][1:] - # ['foo', '*', 'name'] -> ['foo*', 'name'] - if len(l) == 3: - #return [l[0]+l[1], l[2]] - type_arg['type'] = l[0]+l[1] - type_arg['name'] = l[2] - else: - #return l - pass - return type_arg - -def get_params(proto): - """ get the list of parameters from a function prototype - example: proto = "int main (int argc, char ** argv)" - returns: ['int argc', 'char ** argv'] - """ - import re - paramregex = re.compile('[\(, ](\w+ \*?\*? ?\w+)[, \)]') - return paramregex.findall(proto) - -def get_params_types_names(proto): - """ get the list of parameters from a function prototype - example: proto = "int main (int argc, char ** argv)" - returns: [['int', 'argc'], ['char **','argv']] - """ - return map(split_type, get_params(proto)) - -def get_return_type(proto): - import re - paramregex = re.compile('(\w+ ?\*?).*') - outputs = paramregex.findall(proto) - assert len(outputs) == 1 - return outputs[0].replace(' ', '') - -def get_name(proto): - name = proto.split()[1].split('(')[0] - return name.replace('*','') - -# the important bits: the size of the output for each objects. this data should -# move into the C library at some point. -defaultsizes = { - 'resampler': ['input->length * self->ratio'], - 'specdesc': ['1'], - 'onset': ['1'], - 'pitchyin': ['1'], - 'pitchyinfft': ['1'], - 'pitchschmitt': ['1'], - 'pitchmcomb': ['1'], - 'pitchfcomb': ['1'], - 'pitch': ['1'], - 'tss': ['self->buf_size', 'self->buf_size'], - 'mfcc': ['self->n_coeffs'], - 'beattracking': ['self->hop_size'], - 'tempo': ['1'], - 'peakpicker': ['1'], - 'source': ['self->hop_size', '1'], - 'sampler': ['self->hop_size'], - 'wavetable': ['self->hop_size'], -} - -# default value for variables -aubioinitvalue = { - 'uint_t': 0, - 'smpl_t': 0, - 'lsmp_t': 0., - 'char_t*': 'NULL', - } - -aubiodefvalue = { - # we have some clean up to do - 'buf_size': 'Py_default_vector_length', - # and here too - 'hop_size': 'Py_default_vector_length / 2', - # these should be alright - 'samplerate': 'Py_aubio_default_samplerate', - # now for the non obvious ones - 'n_filters': '40', - 'n_coeffs': '13', - 'nelems': '10', - 'flow': '0.', - 'fhig': '1.', - 'ilow': '0.', - 'ihig': '1.', - 'thrs': '0.5', - 'ratio': '0.5', - 'method': '"default"', - 'uri': '"none"', - } - -# aubio to python -aubio2pytypes = { - 'uint_t': 'I', - 'smpl_t': 'f', - 'lsmp_t': 'd', - 'fvec_t*': 'O', - 'cvec_t*': 'O', - 'char_t*': 's', -} - -# python to aubio -aubiovecfrompyobj = { - 'fvec_t*': 'PyAubio_ArrayToCFvec', - 'cvec_t*': 'PyAubio_ArrayToCCvec', - 'uint_t': '(uint_t)PyInt_AsLong', -} - -# aubio to python -aubiovectopyobj = { - 'fvec_t*': 'PyAubio_CFvecToArray', - 'cvec_t*': 'PyAubio_CCvecToPyCvec', - 'smpl_t': 'PyFloat_FromDouble', - 'uint_t*': 'PyInt_FromLong', - 'uint_t': 'PyInt_FromLong', -} - -def gen_new_init(newfunc, name): - newparams = get_params_types_names(newfunc) - # self->param1, self->param2, self->param3 - if len(newparams): - selfparams = ', self->'+', self->'.join([p['name'] for p in newparams]) - else: - selfparams = '' - # "param1", "param2", "param3" - paramnames = ", ".join(["\""+p['name']+"\"" for p in newparams]) - pyparams = "".join(map(lambda p: aubio2pytypes[p['type']], newparams)) - paramrefs = ", ".join(["&" + p['name'] for p in newparams]) - s = """\ -// WARNING: this file is generated, DO NOT EDIT - -// WARNING: if you haven't read the first line yet, please do so -#include "aubiowraphell.h" - -typedef struct -{ - PyObject_HEAD - aubio_%(name)s_t * o; -""" % locals() - for p in newparams: - ptype = p['type'] - pname = p['name'] - s += """\ - %(ptype)s %(pname)s; -""" % locals() - s += """\ -} Py_%(name)s; - -static char Py_%(name)s_doc[] = "%(name)s object"; - -static PyObject * -Py_%(name)s_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) -{ - Py_%(name)s *self; -""" % locals() - for p in newparams: - ptype = p['type'] - pname = p['name'] - initval = aubioinitvalue[ptype] - s += """\ - %(ptype)s %(pname)s = %(initval)s; -""" % locals() - # now the actual PyArg_Parse - if len(paramnames): - s += """\ - static char *kwlist[] = { %(paramnames)s, NULL }; - - if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist, - %(paramrefs)s)) { - return NULL; - } -""" % locals() - s += """\ - - self = (Py_%(name)s *) pytype->tp_alloc (pytype, 0); - - if (self == NULL) { - return NULL; - } -""" % locals() - for p in newparams: - ptype = p['type'] - pname = p['name'] - defval = aubiodefvalue[pname] - if ptype == 'char_t*': - s += """\ - - self->%(pname)s = %(defval)s; - if (%(pname)s != NULL) { - self->%(pname)s = %(pname)s; - } -""" % locals() - elif ptype == 'uint_t': - s += """\ - - self->%(pname)s = %(defval)s; - if ((sint_t)%(pname)s > 0) { - self->%(pname)s = %(pname)s; - } else if ((sint_t)%(pname)s < 0) { - PyErr_SetString (PyExc_ValueError, - "can not use negative value for %(pname)s"); - return NULL; - } -""" % locals() - elif ptype == 'smpl_t': - s += """\ - - self->%(pname)s = %(defval)s; - if (%(pname)s != %(defval)s) { - self->%(pname)s = %(pname)s; - } -""" % locals() - else: - write_msg ("ERROR, unknown type of parameter %s %s" % (ptype, pname) ) - s += """\ - - return (PyObject *) self; -} - -AUBIO_INIT(%(name)s %(selfparams)s) - -AUBIO_DEL(%(name)s) - -""" % locals() - return s - -def gen_do_input_params(inputparams): - inputdefs = '' - parseinput = '' - inputrefs = '' - inputvecs = '' - pytypes = '' - - if len(inputparams): - # build the parsing string for PyArg_ParseTuple - pytypes = "".join([aubio2pytypes[p['type']] for p in inputparams]) - - inputdefs = " /* input vectors python prototypes */\n" - for p in inputparams: - if p['type'] != 'uint_t': - inputdefs += " PyObject * " + p['name'] + "_obj;\n" - - inputvecs = " /* input vectors prototypes */\n " - inputvecs += "\n ".join(map(lambda p: p['type'] + ' ' + p['name'] + ";", inputparams)) - - parseinput = " /* input vectors parsing */\n " - for p in inputparams: - inputvec = p['name'] - if p['type'] != 'uint_t': - inputdef = p['name'] + "_obj" - else: - inputdef = p['name'] - converter = aubiovecfrompyobj[p['type']] - if p['type'] != 'uint_t': - parseinput += """%(inputvec)s = %(converter)s (%(inputdef)s); - - if (%(inputvec)s == NULL) { - return NULL; - } - - """ % locals() - - # build the string for the input objects references - inputreflist = [] - for p in inputparams: - if p['type'] != 'uint_t': - inputreflist += [ "&" + p['name'] + "_obj" ] - else: - inputreflist += [ "&" + p['name'] ] - inputrefs = ", ".join(inputreflist) - # end of inputs strings - return inputdefs, parseinput, inputrefs, inputvecs, pytypes - -def gen_do_output_params(outputparams, name): - outputvecs = "" - outputcreate = "" - if len(outputparams): - outputvecs = " /* output vectors prototypes */\n" - for p in outputparams: - params = { - 'name': p['name'], 'pytype': p['type'], 'autype': p['type'][:-3], - 'length': defaultsizes[name].pop(0) } - if (p['type'] == 'uint_t*'): - outputvecs += ' uint_t' + ' ' + p['name'] + ";\n" - outputcreate += " %(name)s = 0;\n" % params - else: - outputvecs += " " + p['type'] + ' ' + p['name'] + ";\n" - outputcreate += " /* creating output %(name)s as a new_%(autype)s of length %(length)s */\n" % params - outputcreate += " %(name)s = new_%(autype)s (%(length)s);\n" % params - - returnval = ""; - if len(outputparams) > 1: - returnval += " PyObject *outputs = PyList_New(0);\n" - for p in outputparams: - returnval += " PyList_Append( outputs, (PyObject *)" + aubiovectopyobj[p['type']] + " (" + p['name'] + ")" +");\n" - returnval += " return outputs;" - elif len(outputparams) == 1: - if defaultsizes[name] == '1': - returnval += " return (PyObject *)PyFloat_FromDouble(" + p['name'] + "->data[0])" - else: - returnval += " return (PyObject *)" + aubiovectopyobj[p['type']] + " (" + p['name'] + ")" - else: - returnval += " Py_RETURN_NONE" - # end of output strings - return outputvecs, outputcreate, returnval - -def gen_do(dofunc, name): - funcname = dofunc.split()[1].split('(')[0] - doparams = get_params_types_names(dofunc) - # make sure the first parameter is the object - assert doparams[0]['type'] == "aubio_"+name+"_t*", \ - "method is not in 'aubio__t" - # and remove it - doparams = doparams[1:] - - n_param = len(doparams) - - if name in param_numbers.keys(): - n_input_param, n_output_param = param_numbers[name] - else: - n_input_param, n_output_param = 1, n_param - 1 - - assert n_output_param + n_input_param == n_param, "n_output_param + n_input_param != n_param for %s" % name - - inputparams = doparams[:n_input_param] - outputparams = doparams[n_input_param:n_input_param + n_output_param] - - inputdefs, parseinput, inputrefs, inputvecs, pytypes = gen_do_input_params(inputparams); - outputvecs, outputcreate, returnval = gen_do_output_params(outputparams, name) - - # build strings for outputs - # build the parameters for the _do() call - doparams_string = "self->o" - for p in doparams: - if p['type'] == 'uint_t*': - doparams_string += ", &" + p['name'] - else: - doparams_string += ", " + p['name'] - - if n_input_param: - arg_parse_tuple = """\ - if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) { - return NULL; - } -""" % locals() - else: - arg_parse_tuple = "" - # put it all together - s = """\ -/* function Py_%(name)s_do */ -static PyObject * -Py_%(name)s_do(Py_%(name)s * self, PyObject * args) -{ -%(inputdefs)s -%(inputvecs)s -%(outputvecs)s - -%(arg_parse_tuple)s - -%(parseinput)s - -%(outputcreate)s - - /* compute _do function */ - %(funcname)s (%(doparams_string)s); - -%(returnval)s; -} -""" % locals() - return s - -def gen_members(new_method, name): - newparams = get_params_types_names(new_method) - s = """ -AUBIO_MEMBERS_START(%(name)s)""" % locals() - for param in newparams: - if param['type'] == 'char_t*': - s += """ - {"%(pname)s", T_STRING, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \ - % { 'pname': param['name'], 'ptype': param['type'], 'name': name} - elif param['type'] == 'uint_t': - s += """ - {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \ - % { 'pname': param['name'], 'ptype': param['type'], 'name': name} - elif param['type'] == 'smpl_t': - s += """ - {"%(pname)s", T_FLOAT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \ - % { 'pname': param['name'], 'ptype': param['type'], 'name': name} - else: - write_msg ("-- ERROR, unknown member type ", param ) - s += """ -AUBIO_MEMBERS_STOP(%(name)s) - -""" % locals() - return s - - -def gen_methods(get_methods, set_methods, name): - s = "" - method_defs = "" - for method in set_methods: - method_name = get_name(method) - params = get_params_types_names(method) - out_type = get_return_type(method) - assert params[0]['type'] == "aubio_"+name+"_t*", \ - "get method is not in 'aubio__t" - write_msg (method ) - write_msg (params[1:]) - setter_args = "self->o, " +",".join([p['name'] for p in params[1:]]) - parse_args = "" - for p in params[1:]: - parse_args += p['type'] + " " + p['name'] + ";\n" - argmap = "".join([aubio2pytypes[p['type']] for p in params[1:]]) - arglist = ", ".join(["&"+p['name'] for p in params[1:]]) - parse_args += """ - if (!PyArg_ParseTuple (args, "%(argmap)s", %(arglist)s)) { - return NULL; - } """ % locals() - s += """ -static PyObject * -Py%(funcname)s (Py_%(objname)s *self, PyObject *args) -{ - uint_t err = 0; - - %(parse_args)s - - err = %(funcname)s (%(setter_args)s); - - if (err > 0) { - PyErr_SetString (PyExc_ValueError, - "error running %(funcname)s"); - return NULL; - } - Py_RETURN_NONE; -} -""" % {'funcname': method_name, 'objname': name, - 'out_type': out_type, 'setter_args': setter_args, 'parse_args': parse_args } - shortname = method_name.split('aubio_'+name+'_')[-1] - method_defs += """\ - {"%(shortname)s", (PyCFunction) Py%(method_name)s, - METH_VARARGS, ""}, -""" % locals() - - for method in get_methods: - method_name = get_name(method) - params = get_params_types_names(method) - out_type = get_return_type(method) - assert params[0]['type'] == "aubio_"+name+"_t*", \ - "get method is not in 'aubio__t %s" % params[0]['type'] - assert len(params) == 1, \ - "get method has more than one parameter %s" % params - getter_args = "self->o" - returnval = "(PyObject *)" + aubiovectopyobj[out_type] + " (tmp)" - shortname = method_name.split('aubio_'+name+'_')[-1] - method_defs += """\ - {"%(shortname)s", (PyCFunction) Py%(method_name)s, - METH_NOARGS, ""}, -""" % locals() - s += """ -static PyObject * -Py%(funcname)s (Py_%(objname)s *self, PyObject *unused) -{ - %(out_type)s tmp = %(funcname)s (%(getter_args)s); - return %(returnval)s; -} -""" % {'funcname': method_name, 'objname': name, - 'out_type': out_type, 'getter_args': getter_args, 'returnval': returnval } - - s += """ -static PyMethodDef Py_%(name)s_methods[] = { -""" % locals() - s += method_defs - s += """\ - {NULL} /* sentinel */ -}; -""" % locals() - return s - -def gen_finish(name): - s = """\ - -AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s") -""" % locals() - return s diff --git a/python/lib/generator.py b/python/lib/generator.py deleted file mode 100755 index 195c2efb..00000000 --- a/python/lib/generator.py +++ /dev/null @@ -1,226 +0,0 @@ -#! /usr/bin/python - -""" This file generates a c file from a list of cpp prototypes. """ - -import os, sys, shutil -from gen_pyobject import write_msg, gen_new_init, gen_do, gen_members, gen_methods, gen_finish - -def get_cpp_objects(): - - cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=1 -I../build/src ../src/aubio.h').readlines()] - - cpp_output = filter(lambda y: len(y) > 1, cpp_output) - cpp_output = filter(lambda y: not y.startswith('#'), cpp_output) - - i = 1 - while 1: - if i >= len(cpp_output): break - if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'): - cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i] - cpp_output.pop(i-1) - else: - i += 1 - - typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output) - - cpp_objects = [a.split()[3][:-1] for a in typedefs] - - return cpp_output, cpp_objects - -def generate_object_files(output_path): - if os.path.isdir(output_path): shutil.rmtree(output_path) - os.mkdir(output_path) - - generated_objects = [] - cpp_output, cpp_objects = get_cpp_objects() - skip_objects = [ - # already in ext/ - 'fft', - 'pvoc', - 'filter', - 'filterbank', - #'resampler', - # AUBIO_UNSTABLE - 'hist', - 'parameter', - 'scale', - 'beattracking', - 'resampler', - 'sndfile', - 'peakpicker', - 'pitchfcomb', - 'pitchmcomb', - 'pitchschmitt', - 'pitchspecacf', - 'pitchyin', - 'pitchyinfft', - 'sink', - 'sink_apple_audio', - 'sink_sndfile', - 'sink_wavwrite', - 'source', - 'source_apple_audio', - 'source_sndfile', - 'source_avcodec', - 'source_wavread', - #'sampler', - 'audio_unit', - ] - - write_msg("-- INFO: %d objects in total" % len(cpp_objects)) - - for this_object in cpp_objects: - lint = 0 - - if this_object[-2:] == '_t': - object_name = this_object[:-2] - else: - object_name = this_object - write_msg("-- WARNING: %s does not end in _t" % this_object) - - if object_name[:len('aubio_')] != 'aubio_': - write_msg("-- WARNING: %s does not start n aubio_" % this_object) - - write_msg("-- INFO: looking at", object_name) - object_methods = filter(lambda x: this_object in x, cpp_output) - object_methods = [a.strip() for a in object_methods] - object_methods = filter(lambda x: not x.startswith('typedef'), object_methods) - #for method in object_methods: - # write_msg(method) - new_methods = filter(lambda x: 'new_'+object_name in x, object_methods) - if len(new_methods) > 1: - write_msg("-- WARNING: more than one new method for", object_name) - for method in new_methods: - write_msg(method) - elif len(new_methods) < 1: - write_msg("-- WARNING: no new method for", object_name) - elif 0: - for method in new_methods: - write_msg(method) - - del_methods = filter(lambda x: 'del_'+object_name in x, object_methods) - if len(del_methods) > 1: - write_msg("-- WARNING: more than one del method for", object_name) - for method in del_methods: - write_msg(method) - elif len(del_methods) < 1: - write_msg("-- WARNING: no del method for", object_name) - - do_methods = filter(lambda x: object_name+'_do' in x, object_methods) - if len(do_methods) > 1: - pass - #write_msg("-- WARNING: more than one do method for", object_name) - #for method in do_methods: - # write_msg(method) - elif len(do_methods) < 1: - write_msg("-- WARNING: no do method for", object_name) - elif 0: - for method in do_methods: - write_msg(method) - - # check do methods return void - for method in do_methods: - if (method.split()[0] != 'void'): - write_msg("-- ERROR: _do method does not return void:", method ) - - get_methods = filter(lambda x: object_name+'_get_' in x, object_methods) - - set_methods = filter(lambda x: object_name+'_set_' in x, object_methods) - for method in set_methods: - if (method.split()[0] != 'uint_t'): - write_msg("-- ERROR: _set method does not return uint_t:", method ) - - other_methods = filter(lambda x: x not in new_methods, object_methods) - other_methods = filter(lambda x: x not in del_methods, other_methods) - other_methods = filter(lambda x: x not in do_methods, other_methods) - other_methods = filter(lambda x: x not in get_methods, other_methods) - other_methods = filter(lambda x: x not in set_methods, other_methods) - - if len(other_methods) > 0: - write_msg("-- WARNING: some methods for", object_name, "were unidentified") - for method in other_methods: - write_msg(method) - - - # generate this_object - short_name = object_name[len('aubio_'):] - if short_name in skip_objects: - write_msg("-- INFO: skipping object", short_name ) - continue - if 1: #try: - s = gen_new_init(new_methods[0], short_name) - s += gen_do(do_methods[0], short_name) - s += gen_members(new_methods[0], short_name) - s += gen_methods(get_methods, set_methods, short_name) - s += gen_finish(short_name) - generated_filepath = os.path.join(output_path,'gen-'+short_name+'.c') - fd = open(generated_filepath, 'w') - fd.write(s) - #except Exception, e: - # write_msg("-- ERROR:", type(e), str(e), "in", short_name) - # continue - generated_objects += [this_object] - - s = """// generated list of objects created with generator.py - -""" - - types_ready = [] - for each in generated_objects: - types_ready.append(" PyType_Ready (&Py_%sType) < 0" % \ - each.replace('aubio_','').replace('_t','') ) - - s = """// generated list of objects created with generator.py - -#include "aubio-generated.h" -""" - - s += """ -int generated_types_ready (void) -{ - return ( -""" - s += ('\n ||').join(types_ready) - s += """); -} -""" - - s += """ -void add_generated_objects ( PyObject *m ) -{""" - for each in generated_objects: - s += """ - Py_INCREF (&Py_%(name)sType); - PyModule_AddObject (m, "%(name)s", (PyObject *) & Py_%(name)sType);""" % \ - { 'name': ( each.replace('aubio_','').replace('_t','') ) } - - s += """ -}""" - - fd = open(os.path.join(output_path,'aubio-generated.c'), 'w') - fd.write(s) - - s = """// generated list of objects created with generator.py - -#include - -""" - - for each in generated_objects: - s += "extern PyTypeObject Py_%sType;\n" % \ - each.replace('aubio_','').replace('_t','') - - s+= "int generated_objects ( void );\n" - s+= "void add_generated_objects( PyObject *m );\n" - - fd = open(os.path.join(output_path,'aubio-generated.h'), 'w') - fd.write(s) - - from os import listdir - generated_files = listdir(output_path) - generated_files = filter(lambda x: x.endswith('.c'), generated_files) - generated_files = [output_path+'/'+f for f in generated_files] - return generated_files - -if __name__ == '__main__': - generate_object_files('gen') diff --git a/python/lib/moresetuptools.py b/python/lib/moresetuptools.py new file mode 100644 index 00000000..7b0aa1b8 --- /dev/null +++ b/python/lib/moresetuptools.py @@ -0,0 +1,218 @@ +""" A collection of function used from setup.py distutils script """ +# +import sys, os, glob, subprocess +import distutils, distutils.command.clean, distutils.dir_util +from .gen_external import generate_external, header, output_path + +def get_aubio_version(): + # read from VERSION + this_file_dir = os.path.dirname(os.path.abspath(__file__)) + version_file = os.path.join(this_file_dir, '..', '..', 'VERSION') + + if not os.path.isfile(version_file): + raise SystemError("VERSION file not found.") + + for l in open(version_file).readlines(): + #exec (l.strip()) + if l.startswith('AUBIO_MAJOR_VERSION'): + AUBIO_MAJOR_VERSION = int(l.split('=')[1]) + if l.startswith('AUBIO_MINOR_VERSION'): + AUBIO_MINOR_VERSION = int(l.split('=')[1]) + if l.startswith('AUBIO_PATCH_VERSION'): + AUBIO_PATCH_VERSION = int(l.split('=')[1]) + if l.startswith('AUBIO_VERSION_STATUS'): + AUBIO_VERSION_STATUS = l.split('=')[1].strip()[1:-1] + + if AUBIO_MAJOR_VERSION is None or AUBIO_MINOR_VERSION is None \ + or AUBIO_PATCH_VERSION is None: + raise SystemError("Failed parsing VERSION file.") + + verstr = '.'.join(map(str, [AUBIO_MAJOR_VERSION, + AUBIO_MINOR_VERSION, + AUBIO_PATCH_VERSION])) + + if AUBIO_VERSION_STATUS is not None: + verstr += AUBIO_VERSION_STATUS + return verstr + +def get_aubio_pyversion(): + # convert to version for python according to pep 440 + # see https://www.python.org/dev/peps/pep-0440/ + verstr = get_aubio_version() + if '~alpha' in verstr: + verstr = verstr.split('~')[0] + 'a1' + # TODO: add rc, .dev, and .post suffixes, add numbering + return verstr + +# inspired from https://gist.github.com/abergmeier/9488990 +def add_packages(packages, ext=None, **kw): + """ use pkg-config to search which of 'packages' are installed """ + flag_map = { + '-I': 'include_dirs', + '-L': 'library_dirs', + '-l': 'libraries'} + + # if a setuptools extension is passed, fill it with pkg-config results + if ext: + kw = {'include_dirs': ext.include_dirs, + 'extra_link_args': ext.extra_link_args, + 'library_dirs': ext.library_dirs, + 'libraries': ext.libraries, + } + + for package in packages: + print("checking for {:s}".format(package)) + cmd = ['pkg-config', '--libs', '--cflags', package] + try: + tokens = subprocess.check_output(cmd) + except Exception as e: + print("Running \"{:s}\" failed: {:s}".format(' '.join(cmd), repr(e))) + continue + tokens = tokens.decode('utf8').split() + for token in tokens: + key = token[:2] + try: + arg = flag_map[key] + value = token[2:] + except KeyError: + arg = 'extra_link_args' + value = token + kw.setdefault(arg, []).append(value) + for key, value in iter(kw.items()): # remove duplicated + kw[key] = list(set(value)) + return kw + +def add_local_aubio_header(ext): + """ use local "src/aubio.h", not """ + ext.define_macros += [('USE_LOCAL_AUBIO', 1)] + ext.include_dirs += ['src'] # aubio.h + +def add_local_aubio_lib(ext): + """ add locally built libaubio from build/src """ + print("Info: using locally built libaubio") + ext.library_dirs += [os.path.join('build', 'src')] + ext.libraries += ['aubio'] + +def add_local_aubio_sources(ext, usedouble = False): + """ build aubio inside python module instead of linking against libaubio """ + print("Info: libaubio was not installed or built locally with waf, adding src/") + aubio_sources = sorted(glob.glob(os.path.join('src', '**.c'))) + aubio_sources += sorted(glob.glob(os.path.join('src', '*', '**.c'))) + ext.sources += aubio_sources + +def add_local_macros(ext, usedouble = False): + # 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_LIMITS_H', 'HAVE_STDARG_H', + 'HAVE_MEMCPY_HACKS']: + ext.define_macros += [(define_macro, 1)] + +def add_external_deps(ext, usedouble = False): + # loof for additional packages + print("Info: looking for *optional* additional packages") + packages = ['libavcodec', 'libavformat', 'libavutil', 'libavresample', + 'jack', + 'jack', + 'sndfile', + #'fftw3f', + ] + # samplerate only works with float + if usedouble is False: + packages += ['samplerate'] + else: + print("Info: not adding libsamplerate in double precision mode") + add_packages(packages, ext=ext) + if 'avcodec' in ext.libraries \ + and 'avformat' in ext.libraries \ + and 'avutil' in ext.libraries \ + and 'avresample' in ext.libraries: + ext.define_macros += [('HAVE_LIBAV', 1)] + if 'jack' in ext.libraries: + ext.define_macros += [('HAVE_JACK', 1)] + if 'sndfile' in ext.libraries: + ext.define_macros += [('HAVE_SNDFILE', 1)] + if 'samplerate' in ext.libraries: + ext.define_macros += [('HAVE_SAMPLERATE', 1)] + if 'fftw3f' in ext.libraries: + ext.define_macros += [('HAVE_FFTW3F', 1)] + ext.define_macros += [('HAVE_FFTW3', 1)] + + # add accelerate on darwin + if sys.platform.startswith('darwin'): + ext.extra_link_args += ['-framework', 'Accelerate'] + ext.define_macros += [('HAVE_ACCELERATE', 1)] + ext.define_macros += [('HAVE_SOURCE_APPLE_AUDIO', 1)] + ext.define_macros += [('HAVE_SINK_APPLE_AUDIO', 1)] + + if sys.platform.startswith('win'): + ext.define_macros += [('HAVE_WIN_HACKS', 1)] + + ext.define_macros += [('HAVE_WAVWRITE', 1)] + ext.define_macros += [('HAVE_WAVREAD', 1)] + # TODO: + # add cblas + if 0: + ext.libraries += ['cblas'] + ext.define_macros += [('HAVE_ATLAS_CBLAS_H', 1)] + +def add_system_aubio(ext): + # use pkg-config to find aubio's location + aubio_version = get_aubio_version() + add_packages(['aubio = ' + aubio_version], ext) + if 'aubio' not in ext.libraries: + print("Info: aubio " + aubio_version + " was not found by pkg-config") + else: + print("Info: using system aubio " + aubio_version + " found in " + ' '.join(ext.library_dirs)) + +class CleanGenerated(distutils.command.clean.clean): + def run(self): + if os.path.isdir(output_path): + distutils.dir_util.remove_tree(output_path) + +from distutils.command.build_ext import build_ext as _build_ext +class build_ext(_build_ext): + + user_options = _build_ext.user_options + [ + # The format is (long option, short option, description). + ('enable-double', None, 'use HAVE_AUBIO_DOUBLE=1 (default: 0)'), + ] + + def initialize_options(self): + _build_ext.initialize_options(self) + self.enable_double = False + + def finalize_options(self): + _build_ext.finalize_options(self) + if self.enable_double: + self.announce( + 'will generate code for aubio compiled with HAVE_AUBIO_DOUBLE=1', + level=distutils.log.INFO) + + def build_extension(self, extension): + if self.enable_double or 'HAVE_AUBIO_DOUBLE' in os.environ: + extension.define_macros += [('HAVE_AUBIO_DOUBLE', 1)] + enable_double = True + else: + enable_double = False + # seack for aubio headers and lib in PKG_CONFIG_PATH + add_system_aubio(extension) + # the lib was not installed on this system + if 'aubio' not in extension.libraries: + # use local src/aubio.h + if os.path.isfile(os.path.join('src', 'aubio.h')): + add_local_aubio_header(extension) + add_local_macros(extension) + # look for a local waf build + if os.path.isfile(os.path.join('build','src', 'fvec.c.1.o')): + add_local_aubio_lib(extension) + else: + # check for external dependencies + add_external_deps(extension, usedouble=enable_double) + # add libaubio sources and look for optional deps with pkg-config + add_local_aubio_sources(extension, usedouble=enable_double) + # generate files python/gen/*.c, python/gen/aubio-generated.h + extension.sources += generate_external(header, output_path, overwrite = False, + usedouble=enable_double) + return _build_ext.build_extension(self, extension) diff --git a/python/scripts/aubiocut b/python/scripts/aubiocut index c86e883d..aa8ef3fd 100755 --- a/python/scripts/aubiocut +++ b/python/scripts/aubiocut @@ -5,7 +5,6 @@ """ import sys -#from aubio.task import * usage = "usage: %s [options] -i soundfile" % sys.argv[0] usage += "\n help: %s -h" % sys.argv[0] @@ -117,6 +116,10 @@ def parse_args(): metavar = "", action = "store", dest = "cut_until_nsamples", default = None, help="how many extra samples should be added at the end of each slice") + parser.add_option("--cut-every-nslices", type = int, + metavar = "", + action = "store", dest = "cut_every_nslices", default = None, + help="how many slices should be groupped together at each cut") parser.add_option("--cut-until-nslices", type = int, metavar = "", action = "store", dest = "cut_until_nslices", default = None, @@ -134,7 +137,7 @@ def parse_args(): if len(args) == 1: options.source_file = args[0] else: - print "no file name given\n", usage + print ("no file name given\n" + usage) sys.exit(1) return options, args @@ -171,7 +174,7 @@ if __name__ == '__main__': samples, read = s() if o(samples): timestamps.append (o.get_last()) - if options.verbose: print "%.4f" % o.get_last_s() + if options.verbose: print ("%.4f" % o.get_last_s()) total_frames += read if read < hopsize: break del s @@ -187,8 +190,11 @@ if __name__ == '__main__': # 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" + 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 ] diff --git a/python/setup.py b/python/setup.py deleted file mode 100755 index 7c8fd24b..00000000 --- a/python/setup.py +++ /dev/null @@ -1,99 +0,0 @@ -#! /usr/bin/env python - -from setuptools import setup, Extension - -import sys -import os.path -import numpy - -# read from VERSION -for l in open('VERSION').readlines(): exec (l.strip()) -__version__ = '.'.join \ - ([str(x) for x in [AUBIO_MAJOR_VERSION, AUBIO_MINOR_VERSION, AUBIO_PATCH_VERSION]]) \ - + AUBIO_VERSION_STATUS - - -include_dirs = [] -library_dirs = [] -define_macros = [] -extra_link_args = [] - -include_dirs += ['ext'] -include_dirs += [ numpy.get_include() ] - -if sys.platform.startswith('darwin'): - extra_link_args += ['-framework','CoreFoundation', '-framework','AudioToolbox'] - -output_path = 'gen' -generated_object_files = [] - -if not os.path.isdir(output_path): - from lib.generator import generate_object_files - generated_object_files = generate_object_files(output_path) - # define include dirs -else: - import glob - generated_object_files = glob.glob(os.path.join(output_path, '*.c')) -include_dirs += [output_path] - -if os.path.isfile('../src/aubio.h'): - define_macros += [('USE_LOCAL_AUBIO', 1)] - include_dirs += ['../src'] # aubio.h - include_dirs += ['../build/src'] # config.h - library_dirs += ['../build/src'] - -aubio_extension = Extension("aubio._aubio", [ - "ext/aubiomodule.c", - "ext/aubioproxy.c", - "ext/ufuncs.c", - "ext/py-musicutils.c", - "ext/py-cvec.c", - # example without macro - "ext/py-filter.c", - # macroised - "ext/py-filterbank.c", - "ext/py-fft.c", - "ext/py-phasevoc.c", - "ext/py-source.c", - "ext/py-sink.c", - # generated files - ] + generated_object_files, - include_dirs = include_dirs, - library_dirs = library_dirs, - extra_link_args = extra_link_args, - define_macros = define_macros, - libraries=['aubio']) - -classifiers = [ - 'Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Science/Research', - 'Topic :: Software Development :: Libraries', - 'Topic :: Multimedia :: Sound/Audio :: Analysis', - 'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis', - 'Operating System :: POSIX', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Programming Language :: C', - 'Programming Language :: Python', - 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - ] - -distrib = setup(name='aubio', - version = __version__, - packages = ['aubio'], - package_dir = {'aubio':'lib/aubio'}, - scripts = ['scripts/aubiocut'], - ext_modules = [aubio_extension], - description = 'interface to the aubio library', - long_description = 'interface to the aubio library', - license = 'GNU/GPL version 3', - author = 'Paul Brossier', - author_email = 'piem@aubio.org', - maintainer = 'Paul Brossier', - maintainer_email = 'piem@aubio.org', - url = 'http://aubio.org/', - platforms = 'any', - classifiers = classifiers, - install_requires = ['numpy'], - ) diff --git a/python/tests/__init__.py b/python/tests/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/python/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/python/tests/eval_pitch b/python/tests/eval_pitch index 226b927d..f65173f4 100755 --- a/python/tests/eval_pitch +++ b/python/tests/eval_pitch @@ -24,7 +24,7 @@ import sys import time import os.path import numpy -from utils import array_from_text_file, array_from_yaml_file +from .utils import array_from_text_file, array_from_yaml_file from aubio import source, pitch, freqtomidi start = time.time() diff --git a/python/tests/run_all_tests b/python/tests/run_all_tests index 10228e26..bc6bb8c0 100755 --- a/python/tests/run_all_tests +++ b/python/tests/run_all_tests @@ -1,24 +1,5 @@ #! /usr/bin/env python if __name__ == '__main__': - import os, sys, unittest - def load_test(): - # get relevant files - curdir = os.path.dirname(sys.argv[0]) - if curdir == '': curdir = '.' - files = os.listdir(curdir) - modfiles = filter (lambda y: y.endswith('.py'), files) - modfiles = filter (lambda f: f.startswith('test_'), modfiles) - modfiles = filter (lambda y: not 'beattracking' in y, modfiles) - modfiles = filter (lambda y: not 'hist' in y, modfiles) - modfiles = filter (lambda y: not 'scale' in y, modfiles) - modfiles = filter (lambda y: not 'peakpicker' in y, modfiles) - # get module names - modnames = map (lambda x: os.path.splitext(x)[0], modfiles) - # import them - modules = map (__import__, modnames) - # create a test suites from the imported module - load_from_module = unittest.defaultTestLoader.loadTestsFromModule - tests = map(load_from_module, modules) - return unittest.TestSuite(tests) - unittest.main(defaultTest = 'load_test') + import nose2.main + nose2.discover() diff --git a/python/tests/test_aubio.py b/python/tests/test_aubio.py index 951ade98..98a61154 100755 --- a/python/tests/test_aubio.py +++ b/python/tests/test_aubio.py @@ -1,14 +1,19 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite +from unittest import main +from numpy.testing import TestCase class aubiomodule_test_case(TestCase): - def test_import(self): - """ try importing aubio """ - import aubio + def test_import(self): + """ try importing aubio """ + import aubio + + def test_version(self): + """ test aubio.version """ + import aubio + self.assertEqual('0', aubio.version[0]) if __name__ == '__main__': - from unittest import main - main() + main() diff --git a/python/tests/test_cvec.py b/python/tests/test_cvec.py index 1f3c9eca..5632784d 100755 --- a/python/tests/test_cvec.py +++ b/python/tests/test_cvec.py @@ -1,17 +1,18 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite -from numpy.testing import assert_equal, assert_almost_equal -from aubio import cvec -from numpy import array, shape, pi +from unittest import main +import numpy as np +from numpy.testing import TestCase, assert_equal +from aubio import cvec, fvec, float_type + +wrong_type = 'float32' if float_type == 'float64' else 'float64' class aubio_cvec_test_case(TestCase): def test_vector_created_with_zeroes(self): a = cvec(10) - shape(a.norm) - shape(a.phas) - a.norm[0] + assert_equal(a.norm.shape[0], 10 // 2 + 1) + assert_equal(a.phas.shape[0], 10 // 2 + 1) assert_equal(a.norm, 0.) assert_equal(a.phas, 0.) @@ -41,11 +42,103 @@ class aubio_cvec_test_case(TestCase): def test_assign_cvec_phas_slice(self): spec = cvec(1024) - spec.phas[39:-1] = -pi + spec.phas[39:-1] = -np.pi assert_equal(spec.phas[0:39], 0) - assert_equal(spec.phas[39:-1], -pi) + assert_equal(spec.phas[39:-1], -np.pi) assert_equal(spec.norm, 0) + def test_assign_cvec_with_other_cvec(self): + """ check dest cvec is still reachable after source was deleted """ + spec = cvec(1024) + a = np.random.rand(1024//2+1).astype(float_type) + b = np.random.rand(1024//2+1).astype(float_type) + spec.norm = a + spec.phas = b + new_spec = spec + del spec + assert_equal(a, new_spec.norm) + assert_equal(b, new_spec.phas) + assert_equal(id(a), id(new_spec.norm)) + assert_equal(id(b), id(new_spec.phas)) + + def test_pass_to_numpy(self): + spec = cvec(1024) + norm = spec.norm + phas = spec.phas + del spec + new_spec = cvec(1024) + new_spec.norm = norm + new_spec.phas = phas + assert_equal(norm, new_spec.norm) + assert_equal(phas, new_spec.phas) + assert_equal(id(norm), id(new_spec.norm)) + assert_equal(id(phas), id(new_spec.phas)) + del norm + del phas + assert_equal(new_spec.norm, 0.) + assert_equal(new_spec.phas, 0.) + del new_spec + + def test_assign_norm_too_large(self): + a = cvec(512) + b = fvec(512//2+1 + 4) + with self.assertRaises(ValueError): + a.norm = b + + def test_assign_norm_too_small(self): + a = cvec(512) + b = fvec(512//2+1 - 4) + with self.assertRaises(ValueError): + a.norm = b + + def test_assign_phas_too_large(self): + a = cvec(512) + b = fvec(512//2+1 + 4) + with self.assertRaises(ValueError): + a.phas = b + + def test_assign_phas_too_small(self): + a = cvec(512) + b = fvec(512//2+1 - 4) + with self.assertRaises(ValueError): + a.phas = b + + def test_cvec_repr(self): + win_s = 512 + c = cvec(win_s) + expected_repr = "aubio cvec of {:d} elements".format(win_s//2+1) + self.assertEqual(repr(c), expected_repr) + +class aubio_cvec_wrong_norm_input(TestCase): + + def test_wrong_length(self): + with self.assertRaises(ValueError): + cvec(-1) + + def test_set_norm_with_scalar(self): + a = cvec(512) + with self.assertRaises(ValueError): + a.norm = 1 + + def test_set_norm_with_scalar_array(self): + a = cvec(512) + with self.assertRaises(ValueError): + a.norm = np.ndarray(1, dtype = 'int') + + def test_set_norm_with_int_array(self): + a = cvec(512) + with self.assertRaises(ValueError): + a.norm = np.zeros(512//2+1, dtype = 'int') + + def test_set_norm_with_wrong_float_array(self): + a = cvec(512) + with self.assertRaises(ValueError): + a.norm = np.zeros(512//2+1, dtype = wrong_type) + + def test_set_norm_with_wrong_2d_array(self): + a = cvec(512) + with self.assertRaises(ValueError): + a.norm = np.zeros((512//2+1, 2), dtype = float_type) + if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_fft.py b/python/tests/test_fft.py index f8b2bfee..a8f82b9c 100755 --- a/python/tests/test_fft.py +++ b/python/tests/test_fft.py @@ -1,10 +1,12 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite +from unittest import main +from numpy.testing import TestCase from numpy.testing import assert_equal, assert_almost_equal +import numpy as np from aubio import fvec, fft, cvec -from numpy import array, shape -from math import pi +from math import pi, floor +from random import random class aubio_fft_test_case(TestCase): @@ -20,8 +22,9 @@ class aubio_fft_test_case(TestCase): timegrain = fvec(win_s) f = fft (win_s) fftgrain = f (timegrain) - assert_equal (shape(fftgrain.norm), (win_s/2+1,)) - assert_equal (shape(fftgrain.phas), (win_s/2+1,)) + del f + assert_equal (fftgrain.norm.shape, (win_s/2+1,)) + assert_equal (fftgrain.phas.shape, (win_s/2+1,)) def test_zeros(self): """ check the transform of zeros is all zeros """ @@ -30,14 +33,19 @@ class aubio_fft_test_case(TestCase): f = fft (win_s) fftgrain = f (timegrain) assert_equal ( fftgrain.norm, 0 ) - assert_equal ( fftgrain.phas, 0 ) + try: + assert_equal ( fftgrain.phas, 0 ) + except AssertionError: + assert_equal (fftgrain.phas[fftgrain.phas > 0], +pi) + assert_equal (fftgrain.phas[fftgrain.phas < 0], -pi) + assert_equal (np.abs(fftgrain.phas[np.abs(fftgrain.phas) != pi]), 0) + self.skipTest('fft(fvec(%d)).phas != +0, ' % win_s \ + + 'This is expected when using fftw3 on powerpc.') def test_impulse(self): """ check the transform of one impulse at a random place """ - from random import random - from math import floor win_s = 256 - i = floor(random()*win_s) + i = int(floor(random()*win_s)) impulse = pi * random() f = fft(win_s) timegrain = fvec(win_s) @@ -49,27 +57,26 @@ class aubio_fft_test_case(TestCase): assert_equal ( fftgrain.phas >= -pi, True) def test_impulse_negative(self): - """ check the transform of one impulse at a random place """ - from random import random - from math import floor + """ check the transform of a negative impulse at a random place """ win_s = 256 - i = 0 - impulse = -10. + i = int(floor(random()*win_s)) + impulse = -.1 f = fft(win_s) timegrain = fvec(win_s) + timegrain[0] = 0 timegrain[i] = impulse fftgrain = f ( timegrain ) #self.plot_this ( fftgrain.phas ) - assert_almost_equal ( fftgrain.norm, abs(impulse), decimal = 6 ) + assert_almost_equal ( fftgrain.norm, abs(impulse), decimal = 5 ) if impulse < 0: # phase can be pi or -pi, as it is not unwrapped - assert_almost_equal ( abs(fftgrain.phas[1:-1]) , pi, decimal = 6 ) + #assert_almost_equal ( abs(fftgrain.phas[1:-1]) , pi, decimal = 6 ) assert_almost_equal ( fftgrain.phas[0], pi, decimal = 6) - assert_almost_equal ( fftgrain.phas[-1], pi, decimal = 6) + assert_almost_equal ( np.fmod(fftgrain.phas[-1], pi), 0, decimal = 6) else: - assert_equal ( fftgrain.phas[1:-1] == 0, True) - assert_equal ( fftgrain.phas[0] == 0, True) - assert_equal ( fftgrain.phas[-1] == 0, True) + #assert_equal ( fftgrain.phas[1:-1] == 0, True) + assert_equal ( fftgrain.phas[0], 0) + assert_almost_equal ( np.fmod(fftgrain.phas[-1], pi), 0, decimal = 6) # now check the resynthesis synthgrain = f.rdo ( fftgrain ) #self.plot_this ( fftgrain.phas.T ) @@ -95,7 +102,6 @@ class aubio_fft_test_case(TestCase): def test_rdo_before_do(self): """ check running fft.rdo before fft.do works """ win_s = 1024 - impulse = pi f = fft(win_s) fftgrain = cvec(win_s) t = f.rdo( fftgrain ) @@ -106,7 +112,84 @@ class aubio_fft_test_case(TestCase): plot ( this ) show () + def test_local_fftgrain(self): + """ check aubio.fft() result can be accessed after deletion """ + def compute_grain(impulse): + win_s = 1024 + timegrain = fvec(win_s) + timegrain[0] = impulse + f = fft(win_s) + fftgrain = f ( timegrain ) + return fftgrain + impulse = pi + fftgrain = compute_grain(impulse) + assert_equal ( fftgrain.phas[0], 0) + assert_almost_equal ( fftgrain.phas[1], 0) + assert_almost_equal ( fftgrain.norm[0], impulse, decimal = 6 ) + + def test_local_reconstruct(self): + """ check aubio.fft.rdo() result can be accessed after deletion """ + def compute_grain(impulse): + win_s = 1024 + timegrain = fvec(win_s) + timegrain[0] = impulse + f = fft(win_s) + fftgrain = f ( timegrain ) + r = f.rdo(fftgrain) + return r + impulse = pi + r = compute_grain(impulse) + assert_almost_equal ( r[0], impulse, decimal = 6) + assert_almost_equal ( r[1:], 0) + + def test_large_input_timegrain(self): + win_s = 1024 + f = fft(win_s) + t = fvec(win_s + 1) + with self.assertRaises(ValueError): + f(t) + + def test_small_input_timegrain(self): + win_s = 1024 + f = fft(win_s) + t = fvec(1) + with self.assertRaises(ValueError): + f(t) + + def test_large_input_fftgrain(self): + win_s = 1024 + f = fft(win_s) + s = cvec(win_s + 5) + with self.assertRaises(ValueError): + f.rdo(s) + + def test_small_input_fftgrain(self): + win_s = 1024 + f = fft(win_s) + s = cvec(16) + 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() - diff --git a/python/tests/test_filter.py b/python/tests/test_filter.py index da4ef895..6a7a0ded 100755 --- a/python/tests/test_filter.py +++ b/python/tests/test_filter.py @@ -1,74 +1,87 @@ #! /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 numpy import array -from utils import array_from_text_file +from .utils import array_from_text_file class aubio_filter_test_case(TestCase): - def test_members(self): - f = digital_filter() - assert_equal (f.order, 7) - f = digital_filter(5) - assert_equal (f.order, 5) - f(fvec()) - - def test_cweighting_error(self): - f = digital_filter (2) - self.assertRaises ( ValueError, f.set_c_weighting, 44100 ) - f = digital_filter (8) - self.assertRaises ( ValueError, f.set_c_weighting, 44100 ) - f = digital_filter (5) - self.assertRaises ( ValueError, f.set_c_weighting, 4000 ) - f = digital_filter (5) - self.assertRaises ( ValueError, f.set_c_weighting, 193000 ) - f = digital_filter (7) - self.assertRaises ( ValueError, f.set_a_weighting, 193000 ) - f = digital_filter (5) - self.assertRaises ( ValueError, f.set_a_weighting, 192000 ) + def test_members(self): + f = digital_filter() + assert_equal (f.order, 7) + f = digital_filter(5) + assert_equal (f.order, 5) + f(fvec()) - def test_c_weighting(self): - expected = array_from_text_file('c_weighting_test_simple.expected') - f = digital_filter(5) - f.set_c_weighting(44100) - v = fvec(32) - v[12] = .5 - u = f(v) - assert_almost_equal (expected[1], u) + def test_cweighting_error(self): + f = digital_filter (2) + self.assertRaises ( ValueError, f.set_c_weighting, 44100 ) + f = digital_filter (8) + self.assertRaises ( ValueError, f.set_c_weighting, 44100 ) + f = digital_filter (5) + self.assertRaises ( ValueError, f.set_c_weighting, 4000 ) + f = digital_filter (5) + self.assertRaises ( ValueError, f.set_c_weighting, 193000 ) + f = digital_filter (7) + self.assertRaises ( ValueError, f.set_a_weighting, 193000 ) + f = digital_filter (5) + self.assertRaises ( ValueError, f.set_a_weighting, 192000 ) - def test_c_weighting_8000(self): - expected = array_from_text_file('c_weighting_test_simple_8000.expected') - f = digital_filter(5) - f.set_c_weighting(8000) - v = fvec(32) - v[12] = .5 - u = f(v) - assert_almost_equal (expected[1], u) + def test_c_weighting(self): + expected = array_from_text_file('c_weighting_test_simple.expected') + f = digital_filter(5) + f.set_c_weighting(44100) + v = fvec(32) + v[12] = .5 + u = f(v) + assert_almost_equal (expected[1], u) - def test_a_weighting(self): - expected = array_from_text_file('a_weighting_test_simple.expected') - f = digital_filter(7) - f.set_a_weighting(44100) - v = fvec(32) - v[12] = .5 - u = f(v) - assert_almost_equal (expected[1], u) + def test_c_weighting_8000(self): + expected = array_from_text_file('c_weighting_test_simple_8000.expected') + f = digital_filter(5) + f.set_c_weighting(8000) + v = fvec(32) + v[12] = .5 + u = f(v) + assert_almost_equal (expected[1], u) - def test_a_weighting_parted(self): - expected = array_from_text_file('a_weighting_test_simple.expected') - f = digital_filter(7) - f.set_a_weighting(44100) - v = fvec(16) - v[12] = .5 - u = f(v) - assert_almost_equal (expected[1][:16], u) - # one more time - v = fvec(16) - u = f(v) - assert_almost_equal (expected[1][16:], u) + def test_a_weighting(self): + expected = array_from_text_file('a_weighting_test_simple.expected') + f = digital_filter(7) + f.set_a_weighting(44100) + v = fvec(32) + v[12] = .5 + u = f(v) + assert_almost_equal (expected[1], u) -if __name__ == '__main__': - from unittest import main - main() + def test_a_weighting_parted(self): + expected = array_from_text_file('a_weighting_test_simple.expected') + f = digital_filter(7) + f.set_a_weighting(44100) + v = fvec(16) + v[12] = .5 + u = f(v) + assert_almost_equal (expected[1][:16], u) + # one more time + v = fvec(16) + u = f(v) + assert_almost_equal (expected[1][16:], u) + + def test_set_biquad(self): + f = digital_filter(3) + f.set_biquad(0., 0., 0, 0., 0.) + + def test_set_biquad_wrong_order(self): + f = digital_filter(4) + with self.assertRaises(ValueError): + f.set_biquad(0., 0., 0, 0., 0.) +class aubio_filter_wrong_params(TestCase): + + def test_negative_order(self): + with self.assertRaises(ValueError): + digital_filter(-1) + +if __name__ == '__main__': + main() diff --git a/python/tests/test_filterbank.py b/python/tests/test_filterbank.py index 02ae0f3e..3245008b 100755 --- a/python/tests/test_filterbank.py +++ b/python/tests/test_filterbank.py @@ -1,68 +1,84 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite +from unittest import main +from numpy.testing import TestCase from numpy.testing import assert_equal, assert_almost_equal -from numpy import random -from math import pi -from numpy import array -from aubio import cvec, filterbank -from utils import array_from_text_file +import numpy as np +from aubio import cvec, filterbank, float_type +from .utils import array_from_text_file class aubio_filterbank_test_case(TestCase): - def test_members(self): - f = filterbank(40, 512) - assert_equal ([f.n_filters, f.win_s], [40, 512]) - - def test_set_coeffs(self): - f = filterbank(40, 512) - r = random.random([40, 512 / 2 + 1]).astype('float32') - f.set_coeffs(r) - assert_equal (r, f.get_coeffs()) - - def test_phase(self): - f = filterbank(40, 512) - c = cvec(512) - c.phas[:] = pi - assert_equal( f(c), 0); - - def test_norm(self): - f = filterbank(40, 512) - c = cvec(512) - c.norm[:] = 1 - assert_equal( f(c), 0); - - def test_random_norm(self): - f = filterbank(40, 512) - c = cvec(512) - c.norm[:] = random.random((512 / 2 + 1,)).astype('float32') - assert_equal( f(c), 0) - - def test_random_coeffs(self): - f = filterbank(40, 512) - c = cvec(512) - r = random.random([40, 512 / 2 + 1]).astype('float32') - r /= r.sum() - f.set_coeffs(r) - c.norm[:] = random.random((512 / 2 + 1,)).astype('float32') - assert_equal ( f(c) < 1., True ) - assert_equal ( f(c) > 0., True ) - - def test_mfcc_coeffs(self): - f = filterbank(40, 512) - c = cvec(512) - f.set_mel_coeffs_slaney(44100) - c.norm[:] = random.random((512 / 2 + 1,)).astype('float32') - assert_equal ( f(c) < 1., True ) - assert_equal ( f(c) > 0., True ) - - def test_mfcc_coeffs_16000(self): - expected = array_from_text_file('filterbank_mfcc_16000_512.expected') - f = filterbank(40, 512) - f.set_mel_coeffs_slaney(16000) - assert_almost_equal ( expected, f.get_coeffs() ) + def test_members(self): + f = filterbank(40, 512) + assert_equal ([f.n_filters, f.win_s], [40, 512]) -if __name__ == '__main__': - from unittest import main - main() + def test_set_coeffs(self): + f = filterbank(40, 512) + r = np.random.random([40, int(512 / 2) + 1]).astype(float_type) + f.set_coeffs(r) + assert_equal (r, f.get_coeffs()) + + def test_phase(self): + f = filterbank(40, 512) + c = cvec(512) + c.phas[:] = np.pi + assert_equal( f(c), 0); + + def test_norm(self): + f = filterbank(40, 512) + c = cvec(512) + c.norm[:] = 1 + assert_equal( f(c), 0); + + def test_random_norm(self): + f = filterbank(40, 512) + c = cvec(512) + c.norm[:] = np.random.random((int(512 / 2) + 1,)).astype(float_type) + assert_equal( f(c), 0) + + def test_random_coeffs(self): + win_s = 128 + f = filterbank(40, win_s) + c = cvec(win_s) + r = np.random.random([40, int(win_s / 2) + 1]).astype(float_type) + r /= r.sum() + f.set_coeffs(r) + c.norm[:] = np.random.random((int(win_s / 2) + 1,)).astype(float_type) + assert_equal ( f(c) < 1., True ) + assert_equal ( f(c) > 0., True ) + + def test_mfcc_coeffs(self): + f = filterbank(40, 512) + c = cvec(512) + f.set_mel_coeffs_slaney(44100) + c.norm[:] = np.random.random((int(512 / 2) + 1,)).astype(float_type) + assert_equal ( f(c) < 1., True ) + assert_equal ( f(c) > 0., True ) + + def test_mfcc_coeffs_16000(self): + expected = array_from_text_file('filterbank_mfcc_16000_512.expected') + f = filterbank(40, 512) + f.set_mel_coeffs_slaney(16000) + assert_almost_equal ( expected, f.get_coeffs() ) +class aubio_filterbank_wrong_values(TestCase): + + def test_negative_window(self): + self.assertRaises(ValueError, filterbank, 40, -20) + + def test_negative_filters(self): + self.assertRaises(ValueError, filterbank, -40, 1024) + + def test_filterbank_long_cvec(self): + f = filterbank(40, 512) + with self.assertRaises(ValueError): + f(cvec(1024)) + + def test_filterbank_short_cvec(self): + f = filterbank(40, 512) + with self.assertRaises(ValueError): + f(cvec(256)) + +if __name__ == '__main__': + main() diff --git a/python/tests/test_filterbank_mel.py b/python/tests/test_filterbank_mel.py index 532ca0b3..1ce38e9c 100755 --- a/python/tests/test_filterbank_mel.py +++ b/python/tests/test_filterbank_mel.py @@ -1,51 +1,49 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite +from unittest import main +from numpy.testing import TestCase from numpy.testing import assert_equal, assert_almost_equal from numpy import array, shape -from aubio import cvec, filterbank +from aubio import cvec, filterbank, float_type class aubio_filterbank_mel_test_case(TestCase): - def test_slaney(self): - f = filterbank(40, 512) - f.set_mel_coeffs_slaney(16000) - a = f.get_coeffs() - assert_equal(shape (a), (40, 512/2 + 1) ) - - def test_other_slaney(self): - f = filterbank(40, 512*2) - f.set_mel_coeffs_slaney(44100) - a = f.get_coeffs() - #print "sum is", sum(sum(a)) - for win_s in [256, 512, 1024, 2048, 4096]: - f = filterbank(40, win_s) - f.set_mel_coeffs_slaney(320000) - a = f.get_coeffs() - #print "sum is", sum(sum(a)) - - def test_triangle_freqs_zeros(self): - f = filterbank(9, 1024) - freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] - freqs = array(freq_list, dtype = 'float32') - f.set_triangle_bands(freqs, 48000) - f.get_coeffs().T - assert_equal ( f(cvec(1024)), 0) - - def test_triangle_freqs_ones(self): - f = filterbank(9, 1024) - freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] - freqs = array(freq_list, dtype = 'float32') - f.set_triangle_bands(freqs, 48000) - f.get_coeffs().T - spec = cvec(1024) - spec.norm[:] = 1 - assert_almost_equal ( f(spec), - [ 0.02070313, 0.02138672, 0.02127604, 0.02135417, - 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345]) + def test_slaney(self): + f = filterbank(40, 512) + f.set_mel_coeffs_slaney(16000) + a = f.get_coeffs() + assert_equal(shape (a), (40, 512/2 + 1) ) + + def test_other_slaney(self): + f = filterbank(40, 512*2) + f.set_mel_coeffs_slaney(44100) + _ = f.get_coeffs() + #print "sum is", sum(sum(a)) + for win_s in [256, 512, 1024, 2048, 4096]: + f = filterbank(40, win_s) + f.set_mel_coeffs_slaney(32000) + _ = f.get_coeffs() + #print "sum is", sum(sum(a)) + + def test_triangle_freqs_zeros(self): + f = filterbank(9, 1024) + freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] + freqs = array(freq_list, dtype = float_type) + f.set_triangle_bands(freqs, 48000) + _ = f.get_coeffs().T + assert_equal ( f(cvec(1024)), 0) + + def test_triangle_freqs_ones(self): + f = filterbank(9, 1024) + freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000] + freqs = array(freq_list, dtype = float_type) + f.set_triangle_bands(freqs, 48000) + _ = f.get_coeffs().T + spec = cvec(1024) + spec.norm[:] = 1 + assert_almost_equal ( f(spec), + [ 0.02070313, 0.02138672, 0.02127604, 0.02135417, + 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345]) if __name__ == '__main__': - from unittest import main - main() - - + main() diff --git a/python/tests/test_fvec.py b/python/tests/test_fvec.py index 7521b298..4e50f0f0 100755 --- a/python/tests/test_fvec.py +++ b/python/tests/test_fvec.py @@ -1,9 +1,12 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite -from numpy.testing import assert_equal, assert_almost_equal +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 -from numpy import array, shape +from aubio import float_type + +wrong_type = 'float32' if float_type == 'float64' else 'float64' default_size = 512 @@ -11,15 +14,15 @@ class aubio_fvec_test_case(TestCase): def test_vector_created_with_zeroes(self): a = fvec(10) - assert a.dtype == 'float32' + assert a.dtype == float_type assert a.shape == (10,) - assert_equal (a, 0) + assert_equal(a, 0) def test_vector_create_with_list(self): - a = fvec([0,1,2,3]) - assert a.dtype == 'float32' + a = fvec([0, 1, 2, 3]) + assert a.dtype == float_type assert a.shape == (4,) - assert_equal (range(4), a) + assert_equal(list(range(4)), a) def test_vector_assign_element(self): a = fvec(default_size) @@ -34,104 +37,107 @@ class aubio_fvec_test_case(TestCase): def test_vector(self): a = fvec() - a, len(a) #a.length - a[0] - array(a) - a = fvec(10) + len(a) + _ = a[0] + np.array(a) a = fvec(1) - a.T - array(a).T - a = range(len(a)) + a = fvec(10) + _ = a.T - def test_wrong_values(self): - self.assertRaises (ValueError, fvec, -10) - - a = fvec(2) - self.assertRaises (IndexError, a.__getitem__, 3) - self.assertRaises (IndexError, a.__getitem__, 2) +class aubio_fvec_wrong_values(TestCase): - def test_alpha_norm_of_fvec(self): + def test_negative_length(self): + """ test creating fvec with negative length fails (pure python) """ + self.assertRaises(ValueError, fvec, -10) + + def test_zero_length(self): + """ test creating fvec with zero length fails (pure python) """ + self.assertRaises(ValueError, fvec, 0) + + def test_out_of_bound(self): + """ test assiging fvec out of bounds fails (pure python) """ a = fvec(2) - self.assertEquals (alpha_norm(a, 1), 0) - a[0] = 1 - self.assertEquals (alpha_norm(a, 1), 0.5) - a[1] = 1 - self.assertEquals (alpha_norm(a, 1), 1) - a = array([0, 1], dtype='float32') - from math import sqrt - assert_almost_equal (alpha_norm(a, 2), sqrt(2)/2.) - - def test_alpha_norm_of_none(self): - self.assertRaises (ValueError, alpha_norm, None, 1) - - def test_alpha_norm_of_array_of_float32(self): - # check scalar fails - a = array(1, dtype = 'float32') - self.assertRaises (ValueError, alpha_norm, a, 1) - # check 2d array fails - a = array([[2],[4]], dtype = 'float32') - self.assertRaises (ValueError, alpha_norm, a, 1) - # check 1d array - a = array(range(10), dtype = 'float32') - self.assertEquals (alpha_norm(a, 1), 4.5) - - def test_alpha_norm_of_array_of_int(self): - a = array(1, dtype = 'int') - self.assertRaises (ValueError, alpha_norm, a, 1) - a = array([[[1,2],[3,4]]], dtype = 'int') - self.assertRaises (ValueError, alpha_norm, a, 1) - a = array(range(10), dtype = 'int') - self.assertRaises (ValueError, alpha_norm, a, 1) - - def test_alpha_norm_of_array_of_string (self): - a = "hello" - self.assertRaises (ValueError, alpha_norm, a, 1) + self.assertRaises(IndexError, a.__getitem__, 3) + self.assertRaises(IndexError, a.__getitem__, 2) + +class aubio_wrong_fvec_input(TestCase): + """ uses min_removal to test PyAubio_IsValidVector """ + + def test_no_input(self): + self.assertRaises(TypeError, min_removal) + + def test_none(self): + self.assertRaises(ValueError, min_removal, None) + + def test_wrong_scalar(self): + a = np.array(10, dtype=float_type) + self.assertRaises(ValueError, min_removal, a) + + def test_wrong_dimensions(self): + a = np.array([[[1, 2], [3, 4]]], dtype=float_type) + self.assertRaises(ValueError, min_removal, a) + + def test_wrong_array_size(self): + x = np.array([], dtype=float_type) + self.assertRaises(ValueError, min_removal, x) + + def test_wrong_type(self): + a = np.zeros(10, dtype=wrong_type) + self.assertRaises(ValueError, min_removal, a) + + def test_wrong_list_input(self): + self.assertRaises(ValueError, min_removal, [0., 1.]) + + def test_good_input(self): + a = np.zeros(10, dtype=float_type) + assert_equal(np.zeros(10, dtype=float_type), min_removal(a)) + +class aubio_alpha_norm(TestCase): + + def test_alpha_norm_of_random(self): + x = np.random.rand(1024).astype(float_type) + alpha = np.random.rand() * 5. + x_alpha_norm = (np.sum(np.abs(x)**alpha)/len(x))**(1/alpha) + assert_almost_equal(alpha_norm(x, alpha), x_alpha_norm, decimal = 4) + +class aubio_zero_crossing_rate_test(TestCase): def test_zero_crossing_rate(self): - a = array([0,1,-1], dtype='float32') - assert_almost_equal (zero_crossing_rate(a), 1./3. ) - a = array([0.]*100, dtype='float32') - self.assertEquals (zero_crossing_rate(a), 0 ) - a = array([-1.]*100, dtype='float32') - self.assertEquals (zero_crossing_rate(a), 0 ) - a = array([1.]*100, dtype='float32') - self.assertEquals (zero_crossing_rate(a), 0 ) - - def test_alpha_norm_of_array_of_float64(self): - # check scalar fail - a = array(1, dtype = 'float64') - self.assertRaises (ValueError, alpha_norm, a, 1) - # check 3d array fail - a = array([[[1,2],[3,4]]], dtype = 'float64') - self.assertRaises (ValueError, alpha_norm, a, 1) - # check float64 1d array fail - a = array(range(10), dtype = 'float64') - self.assertRaises (ValueError, alpha_norm, a, 1) - # check float64 2d array fail - a = array([range(10), range(10)], dtype = 'float64') - self.assertRaises (ValueError, alpha_norm, a, 1) + a = np.array([0, 1, -1], dtype=float_type) + assert_almost_equal(zero_crossing_rate(a), 1./3.) + + def test_zero_crossing_rate_zeros(self): + a = np.zeros(100, dtype=float_type) + self.assertEqual(zero_crossing_rate(a), 0) + + def test_zero_crossing_rate_minus_ones(self): + a = np.ones(100, dtype=float_type) + self.assertEqual(zero_crossing_rate(a), 0) + + def test_zero_crossing_rate_plus_ones(self): + a = np.ones(100, dtype=float_type) + self.assertEqual(zero_crossing_rate(a), 0) + +class aubio_fvec_min_removal(TestCase): def test_fvec_min_removal_of_array(self): - a = array([20,1,19], dtype='float32') - b = min_removal(a) - assert_equal (array(b), [19, 0, 18]) - assert_equal (b, [19, 0, 18]) - assert_equal (a, b) - a[0] = 0 - assert_equal (a, b) - - def test_fvec_min_removal_of_array_float64(self): - a = array([20,1,19], dtype='float64') - self.assertRaises (ValueError, min_removal, a) - - def test_fvec_min_removal_of_fvec(self): - a = fvec(3) - a = array([20, 1, 19], dtype = 'float32') + a = np.array([20, 1, 19], dtype=float_type) b = min_removal(a) - assert_equal (array(b), [19, 0, 18]) - assert_equal (b, [19, 0, 18]) - assert_equal (a, b) + assert_equal(b, [19, 0, 18]) + +class aubio_fvec_test_memory(TestCase): + + def test_pass_to_numpy(self): + a = fvec(10) + a[:] = 1. + b = a + del a + assert_equal(b, 1.) + c = fvec(10) + c = b + del b + assert_equal(c, 1.) + del c if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_mathutils.py b/python/tests/test_mathutils.py index e61dc8e4..f68fb110 100755 --- a/python/tests/test_mathutils.py +++ b/python/tests/test_mathutils.py @@ -1,5 +1,6 @@ #! /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 @@ -12,7 +13,7 @@ class aubio_mathutils(TestCase): def test_unwrap2pi(self): unwrap2pi(int(23)) unwrap2pi(float(23.)) - unwrap2pi(long(23.)) + unwrap2pi(int(23.)) unwrap2pi(arange(10)) unwrap2pi(arange(10).astype("int")) unwrap2pi(arange(10).astype("float")) @@ -23,13 +24,12 @@ class aubio_mathutils(TestCase): a[:] = 4. unwrap2pi(a) a = pi/100. * arange(-600,600).astype("float") - b = unwrap2pi (a) + unwrap2pi(a) #print zip(a, b) - try: - print unwrap2pi(["23.","24.",25.]) - except Exception, e: - pass + def test_unwrap2pi_fails_on_list(self): + with self.assertRaises((TypeError, NotImplementedError)): + unwrap2pi(["23.","24.",25.]) def test_unwrap2pi_takes_fvec(self): a = fvec(10) @@ -53,7 +53,7 @@ class aubio_mathutils(TestCase): assert ( b <= pi ).all() def test_freqtomidi(self): - a = array(range(-20, 50000, 100) + [ -1e32, 1e32 ]) + a = array(list(range(-20, 50000, 100)) + [ -1e32, 1e32 ]) b = freqtomidi(a) #print zip(a, b) assert_equal ( isnan(array(b)), False ) @@ -61,7 +61,7 @@ class aubio_mathutils(TestCase): assert_equal ( array(b) < 0, False ) def test_miditofreq(self): - a = range(-30, 200) + [-100000, 10000] + a = list(range(-30, 200)) + [-100000, 10000] b = miditofreq(a) #print zip(a, b) assert_equal ( isnan(b), False ) @@ -69,15 +69,15 @@ class aubio_mathutils(TestCase): assert_equal ( b < 0, False ) def test_miditobin(self): - a = range(-30, 200) + [-100000, 10000] - b = [ bintomidi(x, 44100, 512) for x in a ] + a = list(range(-30, 200)) + [-100000, 10000] + b = [ miditobin(x, 44100, 512) for x in a ] #print zip(a, b) assert_equal ( isnan(array(b)), False ) assert_equal ( isinf(array(b)), False ) assert_equal ( array(b) < 0, False ) def test_bintomidi(self): - a = range(-100, 512) + a = list(range(-100, 512)) b = [ bintomidi(x, 44100, 512) for x in a ] #print zip(a, b) assert_equal ( isnan(array(b)), False ) @@ -85,7 +85,7 @@ class aubio_mathutils(TestCase): assert_equal ( array(b) < 0, False ) def test_freqtobin(self): - a = range(-20, 50000, 100) + [ -1e32, 1e32 ] + a = list(range(-20, 50000, 100)) + [ -1e32, 1e32 ] b = [ freqtobin(x, 44100, 512) for x in a ] #print zip(a, b) assert_equal ( isnan(array(b)), False ) @@ -93,7 +93,7 @@ class aubio_mathutils(TestCase): assert_equal ( array(b) < 0, False ) def test_bintofreq(self): - a = range(-20, 148) + a = list(range(-20, 148)) b = [ bintofreq(x, 44100, 512) for x in a ] #print zip(a, b) assert_equal ( isnan(array(b)), False ) @@ -101,5 +101,4 @@ class aubio_mathutils(TestCase): assert_equal ( array(b) < 0, False ) if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_mfcc.py b/python/tests/test_mfcc.py new file mode 100755 index 00000000..e7f3b186 --- /dev/null +++ b/python/tests/test_mfcc.py @@ -0,0 +1,114 @@ +#! /usr/bin/env python + +from nose2 import main +from nose2.tools import params +from numpy import random, count_nonzero +from numpy.testing import TestCase +from aubio import mfcc, cvec, float_type + +buf_size = 2048 +n_filters = 40 +n_coeffs = 13 +samplerate = 44100 + + +new_params = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate'] +new_deflts = [1024, 40, 13, 44100] + +class aubio_mfcc(TestCase): + + def setUp(self): + self.o = mfcc() + + def test_default_creation(self): + pass + + def test_delete(self): + del self.o + + @params(*new_params) + def test_read_only_member(self, name): + o = self.o + with self.assertRaises((TypeError, AttributeError)): + setattr(o, name, 0) + + @params(*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) + +class aubio_mfcc_wrong_params(TestCase): + + def test_wrong_buf_size(self): + with self.assertRaises(ValueError): + mfcc(buf_size = -1) + + def test_wrong_n_filters(self): + with self.assertRaises(ValueError): + mfcc(n_filters = -1) + + def test_wrong_n_coeffs(self): + with self.assertRaises(ValueError): + mfcc(n_coeffs = -1) + + def test_wrong_samplerate(self): + with self.assertRaises(ValueError): + mfcc(samplerate = -1) + + def test_wrong_input_size(self): + m = mfcc(buf_size = 1024) + with self.assertRaises(ValueError): + m(cvec(512)) + +class aubio_mfcc_compute(TestCase): + + def test_members(self): + + o = mfcc(buf_size, n_filters, n_coeffs, samplerate) + #assert_equal ([o.buf_size, o.method], [buf_size, method]) + + spec = cvec(buf_size) + #spec.norm[0] = 1 + #spec.norm[1] = 1./2. + #print "%20s" % method, str(o(spec)) + coeffs = o(spec) + self.assertEqual(coeffs.size, n_coeffs) + #print coeffs + spec.norm = random.random_sample((len(spec.norm),)).astype(float_type) + spec.phas = random.random_sample((len(spec.phas),)).astype(float_type) + #print "%20s" % method, str(o(spec)) + self.assertEqual(count_nonzero(o(spec) != 0.), n_coeffs) + #print coeffs + + +class aubio_mfcc_all_parameters(TestCase): + + @params( + (2048, 40, 13, 44100), + (1024, 40, 13, 44100), + (512, 40, 13, 44100), + (512, 40, 13, 16000), + (256, 40, 13, 16000), + (128, 40, 13, 16000), + (128, 40, 12, 16000), + (128, 40, 13, 15000), + (512, 40, 20, 44100), + (512, 40, 40, 44100), + (512, 40, 3, 44100), + (1024, 40, 20, 44100), + #(1024, 30, 20, 44100), + (1024, 40, 40, 44100), + (1024, 40, 3, 44100), + ) + 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) + spec = cvec(buf_size) + spec.phas[0] = 0.2 + for _ in range(10): + o(spec) + #print coeffs + +if __name__ == '__main__': + main() diff --git a/python/tests/test_midi2note.py b/python/tests/test_midi2note.py index ce854bfb..056738e3 100755 --- a/python/tests/test_midi2note.py +++ b/python/tests/test_midi2note.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- from aubio import midi2note +from nose2.tools import params import unittest list_of_known_midis = ( @@ -16,10 +17,10 @@ list_of_known_midis = ( class midi2note_good_values(unittest.TestCase): - def test_midi2note_known_values(self): + @params(*list_of_known_midis) + def test_midi2note_known_values(self, midi, note): " known values are correctly converted " - for midi, note in list_of_known_midis: - self.assertEqual ( midi2note(midi), note ) + self.assertEqual ( midi2note(midi), note ) class midi2note_wrong_values(unittest.TestCase): @@ -27,7 +28,7 @@ class midi2note_wrong_values(unittest.TestCase): " fails when passed a negative value " self.assertRaises(ValueError, midi2note, -2) - def test_midi2note_negative_value(self): + def test_midi2note_large(self): " fails when passed a value greater than 127 " self.assertRaises(ValueError, midi2note, 128) @@ -40,4 +41,5 @@ class midi2note_wrong_values(unittest.TestCase): self.assertRaises(TypeError, midi2note, "a") if __name__ == '__main__': - unittest.main() + import nose2 + nose2.main() diff --git a/python/tests/test_musicutils.py b/python/tests/test_musicutils.py index 35132895..dd54abb3 100755 --- a/python/tests/test_musicutils.py +++ b/python/tests/test_musicutils.py @@ -1,13 +1,11 @@ #! /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 import cos, arange -from math import pi - from aubio import window, level_lin, db_spl, silence_detection, level_detection - -from aubio import fvec +from aubio import fvec, float_type class aubio_window(TestCase): @@ -15,25 +13,17 @@ class aubio_window(TestCase): window("default", 1024) def test_fail_name_not_string(self): - try: + with self.assertRaises(TypeError): window(10, 1024) - except ValueError, e: - pass - else: - self.fail('non-string window type does not raise a ValueError') def test_fail_size_not_int(self): - try: + with self.assertRaises(TypeError): window("default", "default") - except ValueError, e: - pass - else: - self.fail('non-integer window length does not raise a ValueError') def test_compute_hanning_1024(self): size = 1024 aubio_window = window("hanning", size) - numpy_window = .5 - .5 * cos(2. * pi * arange(size) / size) + numpy_window = .5 - .5 * np.cos(2. * np.pi * np.arange(size) / size) assert_almost_equal(aubio_window, numpy_window) class aubio_level_lin(TestCase): @@ -41,80 +31,58 @@ class aubio_level_lin(TestCase): level_lin(fvec(1024)) def test_fail_not_fvec(self): - try: + with self.assertRaises(ValueError): level_lin("default") - except ValueError, e: - pass - else: - self.fail('non-number input phase does not raise a TypeError') def test_zeros_is_zeros(self): assert_equal(level_lin(fvec(1024)), 0.) def test_minus_ones_is_one(self): - from numpy import ones - assert_equal(level_lin(-ones(1024, dtype="float32")), 1.) + assert_equal(level_lin(-np.ones(1024, dtype = float_type)), 1.) class aubio_db_spl(TestCase): def test_accept_fvec(self): db_spl(fvec(1024)) def test_fail_not_fvec(self): - try: + with self.assertRaises(ValueError): db_spl("default") - except ValueError, e: - pass - else: - self.fail('non-number input phase does not raise a TypeError') def test_zeros_is_inf(self): - from math import isinf - assert isinf(db_spl(fvec(1024))) + assert np.isinf(db_spl(fvec(1024))) def test_minus_ones_is_zero(self): - from numpy import ones - assert_equal(db_spl(-ones(1024, dtype="float32")), 0.) + assert_equal(db_spl(-np.ones(1024, dtype = float_type)), 0.) class aubio_silence_detection(TestCase): def test_accept_fvec(self): silence_detection(fvec(1024), -70.) def test_fail_not_fvec(self): - try: + with self.assertRaises(ValueError): silence_detection("default", -70) - except ValueError, e: - pass - else: - self.fail('non-number input phase does not raise a TypeError') def test_zeros_is_one(self): - from math import isinf assert silence_detection(fvec(1024), -70) == 1 def test_minus_ones_is_zero(self): from numpy import ones - assert silence_detection(ones(1024, dtype="float32"), -70) == 0 + assert silence_detection(ones(1024, dtype = float_type), -70) == 0 class aubio_level_detection(TestCase): def test_accept_fvec(self): level_detection(fvec(1024), -70.) def test_fail_not_fvec(self): - try: + with self.assertRaises(ValueError): level_detection("default", -70) - except ValueError, e: - pass - else: - self.fail('non-number input phase does not raise a TypeError') def test_zeros_is_one(self): - from math import isinf assert level_detection(fvec(1024), -70) == 1 def test_minus_ones_is_zero(self): from numpy import ones - assert level_detection(ones(1024, dtype="float32"), -70) == 0 + assert level_detection(ones(1024, dtype = float_type), -70) == 0 if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_note2midi.py b/python/tests/test_note2midi.py index 77be8940..261b025b 100755 --- a/python/tests/test_note2midi.py +++ b/python/tests/test_note2midi.py @@ -1,7 +1,10 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- -from aubio import note2midi +from __future__ import unicode_literals + +from aubio import note2midi, freq2note +from nose2.tools import params import unittest list_of_known_notes = ( @@ -11,24 +14,57 @@ list_of_known_notes = ( ( 'C3', 48 ), ( 'B3', 59 ), ( 'B#3', 60 ), + ( 'C♯4', 61 ), ( 'A4', 69 ), ( 'A#4', 70 ), + ( 'A♯4', 70 ), + ( 'A\u266f4', 70 ), ( 'Bb4', 70 ), - ( u'B♭4', 70 ), + ( 'B♭4', 70 ), + ( 'B\u266d4', 70 ), ( 'G8', 115 ), - ( u'G♯8', 116 ), + ( 'G♯8', 116 ), ( 'G9', 127 ), - ( u'G\udd2a2', 45 ), - ( u'B\ufffd2', 45 ), - ( u'A♮2', 45 ), + ( 'A♮2', 45 ), + ) + +list_of_known_notes_with_unicode_issues = ( + ('C𝄪4', 62 ), + ('E𝄫4', 62 ), + ) + +list_of_unknown_notes = ( + ( 'G\udd2a2' ), + ( 'B\ufffd2' ), + ( 'B\u266e\u266e2' ), + ( 'B\u266f\u266d3' ), + ( 'B33' ), + ( 'C.3' ), + ( 'A' ), + ( '2' ), ) class note2midi_good_values(unittest.TestCase): - def test_note2midi_known_values(self): + @params(*list_of_known_notes) + def test_note2midi_known_values(self, note, midi): " known values are correctly converted " - for note, midi in list_of_known_notes: + self.assertEqual ( note2midi(note), midi ) + + @params(*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" + try: self.assertEqual ( note2midi(note), midi ) + except UnicodeEncodeError as e: + 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 + if len('\U0001D12A') != 1 and sys.version[0] == '2': + self.skipTest(strres + " | upgrade to Python 3 to fix") + else: + raise class note2midi_wrong_values(unittest.TestCase): @@ -49,12 +85,36 @@ class note2midi_wrong_values(unittest.TestCase): self.assertRaises(ValueError, note2midi, 'CBc') def test_note2midi_out_of_range(self): - " fails when passed a out of range note" + " fails when passed a note out of range" self.assertRaises(ValueError, note2midi, 'A9') + def test_note2midi_wrong_note_name(self): + " fails when passed a note with a wrong name" + self.assertRaises(ValueError, note2midi, 'W9') + + def test_note2midi_low_octave(self): + " fails when passed a note with a too low octave" + self.assertRaises(ValueError, note2midi, 'C-9') + def test_note2midi_wrong_data_type(self): " fails when passed a non-string value " self.assertRaises(TypeError, note2midi, 123) + def test_note2midi_wrong_data_too_long(self): + " fails when passed a note with a note name longer than expected" + self.assertRaises(ValueError, note2midi, 'CB+-3') + + @params(*list_of_unknown_notes) + def test_note2midi_unknown_values(self, note): + " unknown values throw out an error " + self.assertRaises(ValueError, note2midi, note) + +class freq2note_simple_test(unittest.TestCase): + + def test_freq2note(self): + " make sure freq2note(441) == A4 " + self.assertEqual("A4", freq2note(441)) + if __name__ == '__main__': - unittest.main() + import nose2 + nose2.main() diff --git a/python/tests/test_notes.py b/python/tests/test_notes.py new file mode 100755 index 00000000..44aa3e7a --- /dev/null +++ b/python/tests/test_notes.py @@ -0,0 +1,81 @@ +#! /usr/bin/env python + +from unittest import main +from numpy.testing import TestCase, assert_equal, assert_almost_equal +from aubio import notes + +AUBIO_DEFAULT_NOTES_SILENCE = -70. +AUBIO_DEFAULT_NOTES_MINIOI_MS = 30. + +class aubio_notes_default(TestCase): + + def test_members(self): + o = notes() + assert_equal ([o.buf_size, o.hop_size, o.method, o.samplerate], + [1024,512,'default',44100]) + + +class aubio_notes_params(TestCase): + + samplerate = 44100 + + def setUp(self): + self.o = notes(samplerate = self.samplerate) + + def test_get_minioi_ms(self): + assert_equal (self.o.get_minioi_ms(), AUBIO_DEFAULT_NOTES_MINIOI_MS) + + def test_set_minioi_ms(self): + val = 40. + self.o.set_minioi_ms(val) + assert_almost_equal (self.o.get_minioi_ms(), val) + + def test_get_silence(self): + assert_equal (self.o.get_silence(), AUBIO_DEFAULT_NOTES_SILENCE) + + def test_set_silence(self): + val = -50 + 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') + +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 + + s = source(filepath, samplerate, hop_s) + samplerate = s.samplerate + + tolerance = 0.8 + + notes_o = notes("default", win_s, hop_s, samplerate) + total_frames = 0 + + results = [] + while True: + samples, read = s() + new_note = notes_o(samples) + if (new_note[0] != 0): + note_str = ' '.join(["%.2f" % i for i in new_note]) + results.append( [total_frames, np.copy(new_note)] ) + total_frames += read + if read < hop_s: break + return results + + def test_sinewave(self): + for filepath in list_of_sounds: + if '44100Hz_44100f_sine441.wav' in filepath: + results = self.analyze_file(filepath) + assert_equal (len(results), 1) + assert_equal (len(results[0]), 2) + assert_equal (results[0][0], 1280) + assert_equal (results[0][1], [69, 123, -1]) + +if __name__ == '__main__': + main() diff --git a/python/tests/test_onset.py b/python/tests/test_onset.py index 81bf2489..dcb6dabc 100755 --- a/python/tests/test_onset.py +++ b/python/tests/test_onset.py @@ -1,7 +1,7 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite -from numpy.testing import assert_equal, assert_almost_equal +from unittest import main +from numpy.testing import TestCase, assert_equal, assert_almost_equal from aubio import onset class aubio_onset_default(TestCase): @@ -84,5 +84,4 @@ class aubio_onset_8000(aubio_onset_params): samplerate = 8000 if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_phasevoc.py b/python/tests/test_phasevoc.py index 1ffd26c2..957d3b1a 100755 --- a/python/tests/test_phasevoc.py +++ b/python/tests/test_phasevoc.py @@ -1,11 +1,22 @@ #! /usr/bin/env python -from numpy.testing import TestCase, assert_equal, assert_almost_equal -from aubio import fvec, cvec, pvoc -from numpy import array, shape -from numpy.random import random +from numpy.testing import TestCase, assert_equal, assert_array_less +from aubio import fvec, cvec, pvoc, float_type +from nose2 import main +from nose2.tools import params +import numpy as np -precision = 6 +if float_type == 'float32': + max_sq_error = 1.e-12 +else: + max_sq_error = 1.e-29 + +def create_sine(hop_s, freq, samplerate): + t = np.arange(hop_s).astype(float_type) + return np.sin( 2. * np.pi * freq * t / float(samplerate)) + +def create_noise(hop_s): + return np.random.rand(hop_s).astype(float_type) * 2. - 1. class aubio_pvoc_test_case(TestCase): """ pvoc object test case """ @@ -30,53 +41,154 @@ class aubio_pvoc_test_case(TestCase): win_s, hop_s = 1024, 256 f = pvoc (win_s, hop_s) t = fvec (hop_s) - for time in range( 4 * win_s / hop_s ): + for _ in range( int ( 4 * win_s / hop_s ) ): s = f(t) r = f.rdo(s) - assert_equal ( array(t), 0) - assert_equal ( s.norm, 0) - assert_equal ( s.phas, 0) - assert_equal ( r, 0) - - def test_resynth_two_steps(self): - """ check the resynthesis of steps is correct with 50% overlap """ - hop_s = 512 - buf_s = hop_s * 2 + assert_equal ( t, 0.) + assert_equal ( s.norm, 0.) + try: + assert_equal ( s.phas, 0 ) + except AssertionError: + assert_equal (s.phas[s.phas > 0], +np.pi) + assert_equal (s.phas[s.phas < 0], -np.pi) + assert_equal (np.abs(s.phas[np.abs(s.phas) != np.pi]), 0) + self.skipTest('pvoc(fvec(%d)).phas != +0, ' % win_s \ + + 'This is expected when using fftw3 on powerpc.') + assert_equal ( r, 0.) + + @params( + ( 256, 8), + ( 256, 4), + ( 256, 2), + ( 512, 8), + ( 512, 4), + ( 512, 2), + #( 129, 2), + #( 320, 4), + #( 13, 8), + (1024, 8), + (1024, 4), + (1024, 2), + (2048, 8), + (2048, 4), + (2048, 2), + (4096, 8), + (4096, 4), + (4096, 2), + (8192, 8), + (8192, 4), + (8192, 2), + ) + 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( + (44100, 256, 8, 441), + (44100, 256, 4, 1203), + (44100, 256, 2, 3045), + (44100, 512, 8, 445), + (44100, 512, 4, 445), + (44100, 512, 2, 445), + (44100, 1024, 8, 445), + (44100, 1024, 4, 445), + (44100, 1024, 2, 445), + ( 8000, 1024, 2, 445), + (22050, 1024, 2, 445), + (22050, 256, 8, 445), + (96000, 1024, 8, 47000), + (96000, 1024, 8, 20), + ) + 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.reconstruction(sigin, hop_s, ratio) + + def reconstruction(self, sigin, hop_s, ratio): + buf_s = hop_s * ratio f = pvoc(buf_s, hop_s) - sigin = fvec(hop_s) zeros = fvec(hop_s) - # negative step - sigin[20:50] = -.1 - # positive step - sigin[100:200] = .1 - s1 = f(sigin) - r1 = f.rdo(s1) - s2 = f(zeros) - r2 = f.rdo(s2) - #self.plot_this ( s2.norm.T ) - assert_almost_equal ( r2, sigin, decimal = precision ) - - def test_resynth_three_steps(self): - """ check the resynthesis of steps is correct with 25% overlap """ - hop_s = 16 - buf_s = hop_s * 4 - sigin = fvec(hop_s) - zeros = fvec(hop_s) - f = pvoc(buf_s, hop_s) - for i in xrange(hop_s): - sigin[i] = random() * 2. - 1. - t2 = f.rdo( f(sigin) ) - t2 = f.rdo( f(zeros) ) - t2 = f.rdo( f(zeros) ) - t2 = f.rdo( f(zeros) ) - assert_almost_equal( sigin, t2, decimal = precision ) - - def plot_this( self, this ): - from pylab import semilogy, show - semilogy ( this ) - show () + r2 = f.rdo( f(sigin) ) + for _ in range(1, ratio): + r2 = f.rdo( f(zeros) ) + # compute square errors + sq_error = (r2 - sigin)**2 + # make sure all square errors are less than desired precision + assert_array_less(sq_error, max_sq_error) + +class aubio_pvoc_strange_params(TestCase): + + def test_win_size_short(self): + with self.assertRaises(RuntimeError): + pvoc(1, 1) + + def test_hop_size_long(self): + with self.assertRaises(RuntimeError): + pvoc(1024, 1025) + + def test_large_input_timegrain(self): + win_s = 1024 + f = pvoc(win_s) + t = fvec(win_s + 1) + with self.assertRaises(ValueError): + f(t) + + def test_small_input_timegrain(self): + win_s = 1024 + f = pvoc(win_s) + t = fvec(1) + with self.assertRaises(ValueError): + f(t) + + def test_large_input_fftgrain(self): + win_s = 1024 + f = pvoc(win_s) + s = cvec(win_s + 5) + with self.assertRaises(ValueError): + f.rdo(s) + + def test_small_input_fftgrain(self): + win_s = 1024 + f = pvoc(win_s) + s = cvec(16) + with self.assertRaises(ValueError): + f.rdo(s) + +class aubio_pvoc_wrong_params(TestCase): + + def test_wrong_buf_size(self): + win_s = -1 + with self.assertRaises(ValueError): + pvoc(win_s) + + def test_buf_size_too_small(self): + win_s = 1 + with self.assertRaises(RuntimeError): + pvoc(win_s) + + def test_hop_size_negative(self): + win_s = 512 + hop_s = -2 + with self.assertRaises(ValueError): + pvoc(win_s, hop_s) + + def test_hop_size_too_small(self): + win_s = 1 + hop_s = 1 + with self.assertRaises(RuntimeError): + pvoc(win_s, hop_s) + + def test_buf_size_not_power_of_two(self): + win_s = 320 + hop_s = win_s // 2 + try: + with self.assertRaises(RuntimeError): + pvoc(win_s, hop_s) + except AssertionError: + # when compiled with fftw3, aubio supports non power of two fft sizes + self.skipTest('creating aubio.pvoc with size %d did not fail' % win_s) if __name__ == '__main__': - from unittest import main - main() + main() diff --git a/python/tests/test_pitch.py b/python/tests/test_pitch.py index c0c15cd0..00c8eea8 100755 --- a/python/tests/test_pitch.py +++ b/python/tests/test_pitch.py @@ -1,10 +1,9 @@ #! /usr/bin/env python -from unittest import TestCase -from numpy.testing import assert_equal, assert_almost_equal -from numpy import random, sin, arange, mean, median, isnan -from math import pi -from aubio import fvec, pitch, freqtomidi +from unittest import TestCase, main +from numpy.testing import assert_equal +from numpy import sin, arange, mean, median, isnan, pi +from aubio import fvec, pitch, freqtomidi, float_type class aubio_pitch_Good_Values(TestCase): @@ -24,14 +23,14 @@ class aubio_pitch_Good_Values(TestCase): " running on silence gives 0 " p = pitch('default', 2048, 512, 32000) f = fvec (512) - for i in xrange(10): assert_equal (p(f), 0.) + for _ in range(10): assert_equal (p(f), 0.) def test_run_on_ones(self): " running on ones gives 0 " p = pitch('default', 2048, 512, 32000) f = fvec (512) f[:] = 1 - for i in xrange(10): assert_equal (p(f), 0.) + for _ in range(10): assert_equal (p(f), 0.) class aubio_pitch_Sinusoid(TestCase): @@ -50,10 +49,9 @@ class aubio_pitch_Sinusoid(TestCase): self.run_pitch(p, sinvec, freq) def build_sinusoid(self, length, freq, samplerate): - return sin( 2. * pi * arange(length).astype('float32') * freq / samplerate) + return sin( 2. * pi * arange(length).astype(float_type) * freq / samplerate) def run_pitch(self, p, input_vec, freq): - count = 0 pitches, errors = [], [] input_blocks = input_vec.reshape((-1, p.hop_size)) for new_block in input_blocks: @@ -63,7 +61,7 @@ class aubio_pitch_Sinusoid(TestCase): assert_equal ( len(input_blocks), len(pitches) ) assert_equal ( isnan(pitches), False ) # cut the first candidates - cut = ( p.buf_size - p.hop_size ) / p.hop_size + #cut = ( p.buf_size - p.hop_size ) / p.hop_size pitches = pitches[2:] errors = errors[2:] # check that the mean of all relative errors is less than 10% @@ -124,5 +122,4 @@ for algo in pitch_algorithms: setattr (aubio_pitch_Sinusoid, test_method.__name__, test_method) if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_sink.py b/python/tests/test_sink.py index 54749d8e..c31564a2 100755 --- a/python/tests/test_sink.py +++ b/python/tests/test_sink.py @@ -1,17 +1,54 @@ #! /usr/bin/env python -from numpy.testing import TestCase, assert_equal, assert_almost_equal +from nose2 import main +from nose2.tools import params +from numpy.testing import TestCase from aubio import fvec, source, sink -from numpy import array -from utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path +from .utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path + +import warnings +warnings.filterwarnings('ignore', category=UserWarning, append=True) list_of_sounds = list_all_sounds('sounds') +samplerates = [0, 44100, 8000, 32000] +hop_sizes = [512, 1024, 64] + path = None many_files = 300 # 256 opened files is too much +all_params = [] +for soundfile in list_of_sounds: + for hop_size in hop_sizes: + 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\'') + + def test_wrong_filename(self): + with self.assertRaises(RuntimeError): + sink('') + + def test_wrong_samplerate(self): + with self.assertRaises(RuntimeError): + sink(get_tmp_sink_path(), -1) + + def test_wrong_samplerate_too_large(self): + with self.assertRaises(RuntimeError): + sink(get_tmp_sink_path(), 1536001, 2) + + def test_wrong_channels(self): + with self.assertRaises(RuntimeError): + sink(get_tmp_sink_path(), 44100, -1) + + def test_wrong_channels_too_large(self): + with self.assertRaises(RuntimeError): + sink(get_tmp_sink_path(), 44100, 202020) + def test_many_sinks(self): from tempfile import mkdtemp import os.path @@ -23,86 +60,46 @@ class aubio_sink_test_case(TestCase): g = sink(path, 0) sink_list.append(g) write = 32 - for n in range(200): + for _ in range(200): vec = fvec(write) g(vec, write) g.close() shutil.rmtree(tmpdir) - def test_many_sinks_not_closed(self): - from tempfile import mkdtemp - import os.path - import shutil - tmpdir = mkdtemp() - sink_list = [] - try: - for i in range(many_files): - path = os.path.join(tmpdir, 'f-' + str(i) + '.wav') - g = sink(path, 0) - sink_list.append(g) - write = 256 - for n in range(200): - vec = fvec(write) - g(vec, write) - except StandardError: - pass - else: - self.fail("does not fail on too many files open") - for g in sink_list: - g.close() - shutil.rmtree(tmpdir) - - def test_read_and_write(self): + @params(*all_params) + def test_read_and_write(self, hop_size, samplerate, path): - if not len(list_of_sounds): - self.skipTest('add some sound files in \'python/tests/sounds\'') - - for path in list_of_sounds: - for samplerate, hop_size in zip([0, 44100, 8000, 32000], [512, 1024, 64, 256]): - f = source(path, samplerate, hop_size) - if samplerate == 0: samplerate = f.samplerate - sink_path = get_tmp_sink_path() - g = sink(sink_path, samplerate) - total_frames = 0 - while True: - vec, read = f() - g(vec, read) - total_frames += read - if read < f.hop_size: break - if 0: - print "read", "%.2fs" % (total_frames / float(f.samplerate) ), - print "(", total_frames, "frames", "in", - print total_frames / f.hop_size, "blocks", "at", "%dHz" % f.samplerate, ")", - print "from", f.uri, - print "to", g.uri - del_tmp_sink_path(sink_path) - - def test_read_and_write_multi(self): - - if not len(list_of_sounds): - self.skipTest('add some sound files in \'python/tests/sounds\'') + try: + f = source(path, samplerate, hop_size) + except RuntimeError as e: + self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e))) + if samplerate == 0: samplerate = f.samplerate + sink_path = get_tmp_sink_path() + g = sink(sink_path, samplerate) + total_frames = 0 + while True: + vec, read = f() + g(vec, read) + total_frames += read + if read < f.hop_size: break + del_tmp_sink_path(sink_path) - for path in list_of_sounds: - for samplerate, hop_size in zip([0, 44100, 8000, 32000], [512, 1024, 64, 256]): - f = source(path, samplerate, hop_size) - if samplerate == 0: samplerate = f.samplerate - sink_path = get_tmp_sink_path() - g = sink(sink_path, samplerate, channels = f.channels) - total_frames = 0 - while True: - vec, read = f.do_multi() - g.do_multi(vec, read) - total_frames += read - if read < f.hop_size: break - if 0: - print "read", "%.2fs" % (total_frames / float(f.samplerate) ), - print "(", total_frames, "frames", "in", - print f.channels, "channels", "in", - print total_frames / f.hop_size, "blocks", "at", "%dHz" % f.samplerate, ")", - print "from", f.uri, - print "to", g.uri, - print "in", g.channels, "channels" - del_tmp_sink_path(sink_path) + @params(*all_params) + def test_read_and_write_multi(self, hop_size, samplerate, path): + try: + f = source(path, samplerate, hop_size) + except RuntimeError as e: + self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e))) + if samplerate == 0: samplerate = f.samplerate + sink_path = get_tmp_sink_path() + g = sink(sink_path, samplerate, channels = f.channels) + total_frames = 0 + while True: + vec, read = f.do_multi() + g.do_multi(vec, read) + total_frames += read + if read < f.hop_size: break + del_tmp_sink_path(sink_path) def test_close_file(self): samplerate = 44100 @@ -119,6 +116,12 @@ class aubio_sink_test_case(TestCase): g.close() del_tmp_sink_path(sink_path) + def test_read_with(self): + sink_path =get_tmp_sink_path() + vec = fvec(128) + with sink(sink_path, samplerate) as g: + for i in range(10): + g(vec, 128) + if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_slicing.py b/python/tests/test_slicing.py index 8151d617..c96ba52a 100755 --- a/python/tests/test_slicing.py +++ b/python/tests/test_slicing.py @@ -1,10 +1,10 @@ #! /usr/bin/env python -from numpy.testing import TestCase, run_module_suite -from numpy.testing import assert_equal, assert_almost_equal - +from unittest import main +from numpy.testing import TestCase, assert_equal from aubio import slice_source_at_stamps -from utils import * +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 @@ -146,5 +146,4 @@ class aubio_slicing_wrong_ends_test_case(TestCase): shutil.rmtree(self.output_dir) if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_source.py b/python/tests/test_source.py index f571a147..755c01ed 100755 --- a/python/tests/test_source.py +++ b/python/tests/test_source.py @@ -1,120 +1,189 @@ #! /usr/bin/env python -from numpy.testing import TestCase, assert_equal, assert_almost_equal -from aubio import fvec, source -from numpy import array -from utils import list_all_sounds +from nose2 import main +from nose2.tools import params +from numpy.testing import TestCase, assert_equal +from aubio import source +from .utils import list_all_sounds +import numpy as np + +import warnings +warnings.filterwarnings('ignore', category=UserWarning, append=True) list_of_sounds = list_all_sounds('sounds') +samplerates = [0, 44100, 8000, 32000] +hop_sizes = [512, 1024, 64] + path = None +all_params = [] +for soundfile in list_of_sounds: + for hop_size in hop_sizes: + for samplerate in samplerates: + all_params.append((hop_size, samplerate, soundfile)) + + class aubio_source_test_case_base(TestCase): def setUp(self): - if not len(list_of_sounds): self.skipTest('add some sound files in \'python/tests/sounds\'') + if not len(list_of_sounds): + self.skipTest('add some sound files in \'python/tests/sounds\'') + self.default_test_sound = list_of_sounds[0] class aubio_source_test_case(aubio_source_test_case_base): - def test_close_file(self): + @params(*list_of_sounds) + def test_close_file(self, filename): samplerate = 0 # use native samplerate hop_size = 256 - for p in list_of_sounds: - f = source(p, samplerate, hop_size) - f.close() + f = source(filename, samplerate, hop_size) + f.close() - def test_close_file_twice(self): + @params(*list_of_sounds) + def test_close_file_twice(self, filename): samplerate = 0 # use native samplerate hop_size = 256 - for p in list_of_sounds: - f = source(p, samplerate, hop_size) - f.close() - f.close() + f = source(filename, samplerate, hop_size) + f.close() + f.close() class aubio_source_read_test_case(aubio_source_test_case_base): def read_from_source(self, f): total_frames = 0 while True: - vec, read = f() + samples , read = f() total_frames += read - if read < f.hop_size: break - print "read", "%.2fs" % (total_frames / float(f.samplerate) ), - print "(", total_frames, "frames", "in", - print total_frames / f.hop_size, "blocks", "at", "%dHz" % f.samplerate, ")", - print "from", f.uri + if read < f.hop_size: + assert_equal(samples[read:], 0) + break + #result_str = "read {:.2f}s ({:d} frames in {:d} blocks at {:d}Hz) from {:s}" + #result_params = total_frames / float(f.samplerate), total_frames, total_frames//f.hop_size, f.samplerate, f.uri + #print (result_str.format(*result_params)) return total_frames - def test_samplerate_hopsize(self): - for p in list_of_sounds: - for samplerate, hop_size in zip([0, 44100, 8000, 32000], [ 512, 512, 64, 256]): - f = source(p, samplerate, hop_size) - assert f.samplerate != 0 - self.read_from_source(f) - - def test_samplerate_none(self): - for p in list_of_sounds: - f = source(p) - assert f.samplerate != 0 - self.read_from_source(f) - - def test_samplerate_0(self): - for p in list_of_sounds: - f = source(p, 0) - assert f.samplerate != 0 - self.read_from_source(f) + @params(*all_params) + def test_samplerate_hopsize(self, hop_size, samplerate, soundfile): + try: + 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))) + assert f.samplerate != 0 + read_frames = self.read_from_source(f) + if 'f_' in soundfile and samplerate == 0: + import re + f = re.compile('.*_\([0:9]*f\)_.*') + match_f = re.findall('([0-9]*)f_', soundfile) + if len(match_f) == 1: + expected_frames = int(match_f[0]) + self.assertEqual(expected_frames, read_frames) + + @params(*list_of_sounds) + def test_samplerate_none(self, p): + f = source(p) + assert f.samplerate != 0 + self.read_from_source(f) + + @params(*list_of_sounds) + def test_samplerate_0(self, p): + f = source(p, 0) + assert f.samplerate != 0 + self.read_from_source(f) + + @params(*list_of_sounds) + def test_zero_hop_size(self, p): + f = source(p, 0, 0) + assert f.samplerate != 0 + assert f.hop_size != 0 + self.read_from_source(f) + + @params(*list_of_sounds) + def test_seek_to_half(self, p): + from random import randint + f = source(p, 0, 0) + assert f.samplerate != 0 + assert f.hop_size != 0 + a = self.read_from_source(f) + c = randint(0, a) + f.seek(c) + b = self.read_from_source(f) + assert a == b + c + + @params(*list_of_sounds) + def test_duration(self, p): + total_frames = 0 + f = source(p) + duration = f.duration + while True: + _, read = f() + total_frames += read + if read < f.hop_size: break + self.assertEqual(duration, total_frames) + + +class aubio_source_test_wrong_params(TestCase): + + def test_wrong_file(self): + with self.assertRaises(RuntimeError): + source('path_to/unexisting file.mp3') + +class aubio_source_test_wrong_params_with_file(aubio_source_test_case_base): def test_wrong_samplerate(self): - for p in list_of_sounds: - try: - f = source(p, -1) - except ValueError, e: - pass - else: - self.fail('negative samplerate does not raise ValueError') + with self.assertRaises(ValueError): + source(self.default_test_sound, -1) def test_wrong_hop_size(self): - for p in list_of_sounds: - try: - f = source(p, 0, -1) - except ValueError, e: - pass - else: - self.fail('negative hop_size does not raise ValueError') - - def test_zero_hop_size(self): - for p in list_of_sounds: - f = source(p, 0, 0) - assert f.samplerate != 0 - assert f.hop_size != 0 - self.read_from_source(f) - - def test_seek_to_half(self): - from random import randint - for p in list_of_sounds: - f = source(p, 0, 0) - assert f.samplerate != 0 - assert f.hop_size != 0 - a = self.read_from_source(f) - c = randint(0, a) - f.seek(c) - b = self.read_from_source(f) - assert a == b + c + with self.assertRaises(ValueError): + source(self.default_test_sound, 0, -1) + + def test_wrong_channels(self): + with self.assertRaises(ValueError): + source(self.default_test_sound, 0, 0, -1) + + def test_wrong_seek(self): + f = source(self.default_test_sound) + with self.assertRaises(ValueError): + f.seek(-1) + + def test_wrong_seek_too_large(self): + f = source(self.default_test_sound) + try: + with self.assertRaises(ValueError): + f.seek(f.duration + f.samplerate * 10) + except AssertionError: + self.skipTest('seeking after end of stream failed raising ValueError') class aubio_source_readmulti_test_case(aubio_source_read_test_case): def read_from_source(self, f): total_frames = 0 while True: - vec, read = f.do_multi() + samples, read = f.do_multi() total_frames += read - if read < f.hop_size: break - print "read", "%.2fs" % (total_frames / float(f.samplerate) ), - print "(", total_frames, "frames", "in", - print f.channels, "channels and", - print total_frames / f.hop_size, "blocks", "at", "%dHz" % f.samplerate, ")", - print "from", f.uri + if read < f.hop_size: + assert_equal(samples[:,read:], 0) + break + #result_str = "read {:.2f}s ({:d} frames in {:d} channels and {:d} blocks at {:d}Hz) from {:s}" + #result_params = total_frames / float(f.samplerate), total_frames, f.channels, int(total_frames/f.hop_size), f.samplerate, f.uri + #print (result_str.format(*result_params)) return total_frames +class aubio_source_with(aubio_source_test_case_base): + + #@params(*list_of_sounds) + @params(*list_of_sounds) + def test_read_from_mono(self, filename): + total_frames = 0 + hop_size = 2048 + with source(filename, 0, hop_size) as input_source: + assert_equal(input_source.hop_size, hop_size) + #assert_equal(input_source.samplerate, samplerate) + total_frames = 0 + for frames in input_source: + total_frames += frames.shape[-1] + # check we read as many samples as we expected + assert_equal(total_frames, input_source.duration) + if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_specdesc.py b/python/tests/test_specdesc.py index 4ec98306..d7769dce 100755 --- a/python/tests/test_specdesc.py +++ b/python/tests/test_specdesc.py @@ -1,9 +1,9 @@ #! /usr/bin/env python +from unittest import main from numpy.testing import TestCase, assert_equal, assert_almost_equal from numpy import random, arange, log, zeros -from aubio import specdesc, cvec -from math import pi +from aubio import specdesc, cvec, float_type methods = ["default", "energy", @@ -29,38 +29,25 @@ class aubio_specdesc(TestCase): o = specdesc() for method in methods: - o = specdesc(method, buf_size) - assert_equal ([o.buf_size, o.method], [buf_size, method]) - - spec = cvec(buf_size) - spec.norm[0] = 1 - spec.norm[1] = 1./2. - #print "%20s" % method, str(o(spec)) - o(spec) - spec.norm = random.random_sample((len(spec.norm),)).astype('float32') - spec.phas = random.random_sample((len(spec.phas),)).astype('float32') - #print "%20s" % method, str(o(spec)) - assert (o(spec) != 0.) - - def test_hfc(self): - o = specdesc("hfc", buf_size) - spec = cvec(buf_size) - # hfc of zeros is zero - assert_equal (o(spec), 0.) - # hfc of ones is sum of all bin numbers - spec.norm[:] = 1 - expected = sum(range(buf_size/2 + 2)) - assert_equal (o(spec), expected) - # changing phase doesn't change anything - spec.phas[:] = 1 - assert_equal (o(spec), sum(range(buf_size/2 + 2))) + o = specdesc(method, buf_size) + assert_equal ([o.buf_size, o.method], [buf_size, method]) + + spec = cvec(buf_size) + spec.norm[0] = 1 + spec.norm[1] = 1./2. + #print "%20s" % method, str(o(spec)) + o(spec) + spec.norm = random.random_sample((len(spec.norm),)).astype(float_type) + spec.phas = random.random_sample((len(spec.phas),)).astype(float_type) + #print "%20s" % method, str(o(spec)) + assert (o(spec) != 0.) def test_phase(self): o = specdesc("phase", buf_size) spec = cvec(buf_size) # phase of zeros is zero assert_equal (o(spec), 0.) - spec.phas = random.random_sample((len(spec.phas),)).astype('float32') + spec.phas = random.random_sample((len(spec.phas),)).astype(float_type) # phase of random is not zero spec.norm[:] = 1 assert (o(spec) != 0.) @@ -70,7 +57,7 @@ class aubio_specdesc(TestCase): spec = cvec(buf_size) # specdiff of zeros is zero assert_equal (o(spec), 0.) - spec.phas = random.random_sample((len(spec.phas),)).astype('float32') + spec.phas = random.random_sample((len(spec.phas),)).astype(float_type) # phase of random is not zero spec.norm[:] = 1 assert (o(spec) != 0.) @@ -79,7 +66,7 @@ class aubio_specdesc(TestCase): o = specdesc("hfc") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a assert_equal (a, c.norm) assert_equal ( sum(a*(a+1)), o(c)) @@ -88,7 +75,7 @@ class aubio_specdesc(TestCase): o = specdesc("complex") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a assert_equal (a, c.norm) # the previous run was on zeros, so previous frames are still 0 @@ -101,7 +88,7 @@ class aubio_specdesc(TestCase): o = specdesc("kl") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a assert_almost_equal( sum(a * log(1.+ a/1.e-1 ) ) / o(c), 1., decimal=6) @@ -109,7 +96,7 @@ class aubio_specdesc(TestCase): o = specdesc("mkl") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a assert_almost_equal( sum(log(1.+ a/1.e-1 ) ) / o(c), 1, decimal=6) @@ -117,11 +104,11 @@ class aubio_specdesc(TestCase): o = specdesc("specflux") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a assert_equal( sum(a), o(c)) assert_equal( 0, o(c)) - c.norm = zeros(c.length, dtype='float32') + c.norm = zeros(c.length, dtype=float_type) assert_equal( 0, o(c)) def test_centroid(self): @@ -129,7 +116,7 @@ class aubio_specdesc(TestCase): c = cvec() # make sure centroid of zeros is zero assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a centroid = sum(a*a) / sum(a) assert_almost_equal (centroid, o(c), decimal = 2) @@ -139,8 +126,8 @@ class aubio_specdesc(TestCase): def test_spread(self): o = specdesc("spread") - c = cvec(2048) - ramp = arange(c.length, dtype='float32') + c = cvec(1024) + ramp = arange(c.length, dtype=float_type) assert_equal( 0., o(c)) a = ramp @@ -153,7 +140,7 @@ class aubio_specdesc(TestCase): o = specdesc("skewness") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a centroid = sum(a*a) / sum(a) spread = sum( (a - centroid)**2 *a) / sum(a) @@ -167,7 +154,7 @@ class aubio_specdesc(TestCase): o = specdesc("kurtosis") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length, dtype='float32') + a = arange(c.length, dtype=float_type) c.norm = a centroid = sum(a*a) / sum(a) spread = sum( (a - centroid)**2 *a) / sum(a) @@ -178,22 +165,22 @@ class aubio_specdesc(TestCase): o = specdesc("slope") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length * 2, 0, -2, dtype='float32') - k = arange(c.length, dtype='float32') + a = arange(c.length * 2, 0, -2, dtype=float_type) + k = arange(c.length, dtype=float_type) c.norm = a num = len(a) * sum(k*a) - sum(k)*sum(a) den = (len(a) * sum(k**2) - sum(k)**2) slope = num/den/sum(a) assert_almost_equal (slope, o(c), decimal = 5) - a = arange(0, c.length * 2, +2, dtype='float32') + a = arange(0, c.length * 2, +2, dtype=float_type) c.norm = a num = len(a) * sum(k*a) - sum(k)*sum(a) den = (len(a) * sum(k**2) - sum(k)**2) slope = num/den/sum(a) assert_almost_equal (slope, o(c), decimal = 5) - a = arange(0, c.length * 2, +2, dtype='float32') + a = arange(0, c.length * 2, +2, dtype=float_type) c.norm = a * 2 assert_almost_equal (slope, o(c), decimal = 5) @@ -201,18 +188,18 @@ class aubio_specdesc(TestCase): o = specdesc("decrease") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length * 2, 0, -2, dtype='float32') - k = arange(c.length, dtype='float32') + a = arange(c.length * 2, 0, -2, dtype=float_type) + k = arange(c.length, dtype=float_type) c.norm = a decrease = sum((a[1:] - a [0]) / k[1:]) / sum(a[1:]) assert_almost_equal (decrease, o(c), decimal = 5) - a = arange(0, c.length * 2, +2, dtype='float32') + a = arange(0, c.length * 2, +2, dtype=float_type) c.norm = a decrease = sum((a[1:] - a [0]) / k[1:]) / sum(a[1:]) assert_almost_equal (decrease, o(c), decimal = 5) - a = arange(0, c.length * 2, +2, dtype='float32') + a = arange(0, c.length * 2, +2, dtype=float_type) c.norm = a * 2 decrease = sum((a[1:] - a [0]) / k[1:]) / sum(a[1:]) assert_almost_equal (decrease, o(c), decimal = 5) @@ -221,18 +208,25 @@ class aubio_specdesc(TestCase): o = specdesc("rolloff") c = cvec() assert_equal( 0., o(c)) - a = arange(c.length * 2, 0, -2, dtype='float32') - k = arange(c.length, dtype='float32') + a = arange(c.length * 2, 0, -2, dtype=float_type) c.norm = a cumsum = .95*sum(a*a) i = 0; rollsum = 0 while rollsum < cumsum: - rollsum += a[i]*a[i] - i+=1 + rollsum += a[i]*a[i] + i+=1 rolloff = i assert_equal (rolloff, o(c)) +class aubio_specdesc_wrong(TestCase): + + def test_negative(self): + with self.assertRaises(ValueError): + specdesc("default", -10) + + def test_unknown(self): + with self.assertRaises(RuntimeError): + specdesc("unknown", 512) if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/test_zero_crossing_rate.py b/python/tests/test_zero_crossing_rate.py index 06cd6dc5..7f6d479c 100755 --- a/python/tests/test_zero_crossing_rate.py +++ b/python/tests/test_zero_crossing_rate.py @@ -1,7 +1,7 @@ #! /usr/bin/env python +from unittest import main from numpy.testing import TestCase - from aubio import fvec, zero_crossing_rate buf_size = 2048 @@ -22,26 +22,25 @@ class zero_crossing_rate_test_case(TestCase): def test_impulse(self): """ check zero crossing rate on a buffer with an impulse """ - self.vector[buf_size / 2] = 1. + self.vector[int(buf_size / 2)] = 1. self.assertEqual(0., zero_crossing_rate(self.vector)) def test_negative_impulse(self): """ check zero crossing rate on a buffer with a negative impulse """ - self.vector[buf_size / 2] = -1. + self.vector[int(buf_size / 2)] = -1. self.assertEqual(2./buf_size, zero_crossing_rate(self.vector)) def test_single(self): """ check zero crossing rate on single crossing """ - self.vector[buf_size / 2 - 1] = 1. - self.vector[buf_size / 2] = -1. + self.vector[int(buf_size / 2) - 1] = 1. + self.vector[int(buf_size / 2)] = -1. self.assertEqual(2./buf_size, zero_crossing_rate(self.vector)) def test_single_with_gap(self): """ check zero crossing rate on single crossing with a gap""" - self.vector[buf_size / 2 - 2] = 1. - self.vector[buf_size / 2] = -1. + self.vector[int(buf_size / 2) - 2] = 1. + self.vector[int(buf_size / 2)] = -1. self.assertEqual(2./buf_size, zero_crossing_rate(self.vector)) if __name__ == '__main__': - from unittest import main main() diff --git a/python/tests/utils.py b/python/tests/utils.py index 1cec0530..b0963fc4 100644 --- a/python/tests/utils.py +++ b/python/tests/utils.py @@ -1,14 +1,20 @@ #! /usr/bin/env python +import os +import glob +import numpy as np +from tempfile import mkstemp + +DEFAULT_SOUND = '22050Hz_5s_brownnoise.wav' + def array_from_text_file(filename, dtype = 'float'): - import os.path - from numpy import array filename = os.path.join(os.path.dirname(__file__), filename) - return array([line.split() for line in open(filename).readlines()], - dtype = dtype) + with open(filename) as f: + lines = f.readlines() + return np.array([line.split() for line in lines], + dtype = dtype) def list_all_sounds(rel_dir): - import os.path, glob datadir = os.path.join(os.path.dirname(__file__), rel_dir) return glob.glob(os.path.join(datadir,'*.*')) @@ -17,18 +23,28 @@ def get_default_test_sound(TestCase, rel_dir = 'sounds'): if len(all_sounds) == 0: TestCase.skipTest("please add some sounds in \'python/tests/sounds\'") else: - return all_sounds[0] + default_sound = all_sounds[0] + if DEFAULT_SOUND in map(os.path.basename, all_sounds): + while os.path.basename(default_sound) != DEFAULT_SOUND: + default_sound = all_sounds.pop(0) + return default_sound def get_tmp_sink_path(): - from tempfile import mkstemp - import os fd, path = mkstemp() os.close(fd) return path def del_tmp_sink_path(path): - import os - os.unlink(path) + try: + os.unlink(path) + except WindowsError as e: + print("deleting {:s} failed ({:s}), reopening".format(path, repr(e))) + with open(path, 'wb') as f: + f.close() + try: + os.unlink(path) + except WindowsError as f: + print("deleting {:s} failed ({:s}), aborting".format(path, repr(e))) def array_from_yaml_file(filename): import yaml @@ -43,13 +59,12 @@ def count_samples_in_file(file_path): s = source(file_path, 0, hopsize) total_frames = 0 while True: - samples, read = s() + _, read = s() total_frames += read if read < hopsize: break return total_frames def count_samples_in_directory(samples_dir): - import os total_frames = 0 for f in os.walk(samples_dir): if len(f[2]): @@ -60,7 +75,6 @@ def count_samples_in_directory(samples_dir): return total_frames def count_files_in_directory(samples_dir): - import os total_files = 0 for f in os.walk(samples_dir): if len(f[2]): diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..99ce0ab5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy +nose2 diff --git a/scripts/apple/Info.plist b/scripts/apple/Info.plist new file mode 100644 index 00000000..e5ac0ba1 --- /dev/null +++ b/scripts/apple/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.aubio.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2015 Paul Brossier. All rights reserved. + NSPrincipalClass + + + diff --git a/scripts/apple/Modules/module.modulemap b/scripts/apple/Modules/module.modulemap new file mode 100644 index 00000000..0fba24cb --- /dev/null +++ b/scripts/apple/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module aubio { + umbrella header "aubio.h" + + export * + module * { export * } +} diff --git a/scripts/build_android b/scripts/build_android new file mode 100755 index 00000000..e1e790cd --- /dev/null +++ b/scripts/build_android @@ -0,0 +1,41 @@ +#! /bin/bash + +set -e +set -x + +# location of android NDK +NDK_PATH=$PWD/../contrib/android-ndk-r12 + +WAFOPTS="--disable-avcodec --disable-samplerate --disable-jack --disable-sndfile" + +# set these variables to change the default values +[ -z $PLATFORM ] && PLATFORM=android-19 +[ -z $ARCH ] && ARCH=arm + +# location nof the standalone toolchains, created with +# $NDK_PATH/build/tools/make-standalone-toolchains.sh +NDK_TOOLCHAINS=$PWD/contrib + +# location of the current toolchain +CURRENT_TOOLCHAIN=$NDK_TOOLCHAINS/toolchain-$PLATFORM-$ARCH + +# if it does not exist, create the toolchain +[ -d $CURRENT_TOOLCHAIN ] || \ + $NDK_PATH/build/tools/make-standalone-toolchain.sh \ + --platform=$PLATFORM --arch=$ARCH \ + --install-dir=$CURRENT_TOOLCHAIN + +# aubio install destination directory +DESTDIR=$PWD/dist-$PLATFORM-$ARCH + +# wipe it out if it exists +[ -d $DESTDIR ] && rm -rf $DESTDIR + +# get the link to gcc +CC=`ls $CURRENT_TOOLCHAIN/*-linux-android*/bin/gcc` + +CFLAGS="-Os" \ + CC=$CC \ + ./waf distclean configure build install --destdir=$DESTDIR \ + --verbose \ + --with-target-platform=android $WAFOPTS diff --git a/scripts/build_apple_frameworks b/scripts/build_apple_frameworks new file mode 100755 index 00000000..35a53d58 --- /dev/null +++ b/scripts/build_apple_frameworks @@ -0,0 +1,103 @@ +#! /bin/sh + +# cd to aubio directory for consistency +cd `dirname $0`/.. + +AUBIO_TMPDIR=`mktemp -d /var/tmp/aubio-build-XXXX` +PACKAGE=aubio +source VERSION +VERSION=$AUBIO_MAJOR_VERSION.$AUBIO_MINOR_VERSION.$AUBIO_PATCH_VERSION$AUBIO_VERSION_STATUS +LIBVERSION=$LIBAUBIO_LT_CUR.$LIBAUBIO_LT_REV.$LIBAUBIO_LT_AGE +OUTPUTDIR=$PWD/dist +mkdir -p "$OUTPUTDIR" +# add git abbreviated commit hash +#VERSION+=+$(git log --pretty=format:"%h" -1) + +CFLAGS="-Werror -Ofast" +WAFCONF="--disable-sndfile --disable-avcodec --disable-samplerate --enable-fat" # --disable-memcpy --disable-accelerate" + +export VERSION + +function cleanup () { + rm -rf $AUBIO_TMPDIR +} + +trap cleanup SIGINT SIGTERM + +function create_tarballs() { + # name version platform + # create tarball + tarfile=$OUTPUTDIR/$1-$2.$3_binary.tar.bz2 + tar -C $AUBIO_TMPDIR/dist-$3/ -jcf "$tarfile" . + #rm -rf $AUBIO_TMPDIR/dist-$3 +} + +function create_framework() { + rm -rf $AUBIO_TMPDIR/framework-$3 + mkdir -p $AUBIO_TMPDIR/framework-$3/$1-$2.$3_framework/$1.framework + cp -pr COPYING README.md $AUBIO_TMPDIR/framework-$3/$1-$2.$3_framework + pushd $AUBIO_TMPDIR/framework-$3 + cp -pr "$OLDPWD/build/src/lib$1.a" $1-$2.$3_framework/$1.framework/$1 || \ + cp -pr $AUBIO_TMPDIR/dist-$3/usr/local/lib/lib$1.$LIBVERSION.dylib \ + $AUBIO_TMPDIR/framework-$3/$1-$2.$3_framework/$1.framework/$1 + cp -pr $AUBIO_TMPDIR/dist-$3/usr/local/include/$1 $1-$2.$3_framework/$1.framework/Headers + cp -pr "$OLDPWD/scripts/apple/Modules" $1-$2.$3_framework/$1.framework/ + popd +} + +function create_framework_fat() { + rm -rf $AUBIO_TMPDIR/framework-$3 + mkdir -p $AUBIO_TMPDIR/framework-$3/$1-$2.$3_framework/$1.framework + cp -pr COPYING README.md $AUBIO_TMPDIR/framework-$3/$1-$2.$3_framework + pushd $AUBIO_TMPDIR/framework-$3 + cp -pr $AUBIO_TMPDIR/framework-ios/$1-$2.ios_framework/$1.framework/Headers $1-$2.$3_framework/$1.framework + cp -pr $AUBIO_TMPDIR/framework-ios/$1-$2.ios_framework/$1.framework/Modules $1-$2.$3_framework/$1.framework + lipo $AUBIO_TMPDIR/framework-ios/$1-$2.ios_framework/$1.framework/$1 \ + $AUBIO_TMPDIR/framework-iosimulator/$1-$2.iosimulator_framework/$1.framework/$1 \ + -output $1-$2.$3_framework/$1.framework/$1 -create + popd +} + +function create_framework_zip() { + # create zip + pushd $AUBIO_TMPDIR/framework-$3 + zipfile=$1-$2.$3_framework.zip + zip -qr $zipfile $1-$2.$3_framework + popd + mv $AUBIO_TMPDIR/framework-$3/$zipfile "$OUTPUTDIR" +} + +set -x +set -e + +#./waf dist --verbose + +for PLATFORM in darwin ios iosimulator +do + rm -rf $AUBIO_TMPDIR/dist-$PLATFORM + WAF_OPTIONS="--verbose --destdir $AUBIO_TMPDIR/dist-$PLATFORM --with-target-platform $PLATFORM $WAFCONF" + for target in distclean configure build install + do + CFLAGS="$CFLAGS" ./waf $target $WAF_OPTIONS + done + + create_framework $PACKAGE $VERSION $PLATFORM + if [ $PLATFORM == 'darwin' ] + then + # on darwin, build a .tar.bz2 of /usr and a .zip of aubio.framework + create_tarballs $PACKAGE $VERSION $PLATFORM + create_framework_zip $PACKAGE $VERSION $PLATFORM + fi + ./waf uninstall $WAF_OPTIONS + +done + +# after both ios and iosimulator have been built +PLATFORM=iosuniversal +create_framework_fat $PACKAGE $VERSION $PLATFORM +create_framework_zip $PACKAGE $VERSION $PLATFORM + +./waf clean +./waf distclean + +cleanup diff --git a/scripts/build_emscripten b/scripts/build_emscripten new file mode 100755 index 00000000..9d4fc547 --- /dev/null +++ b/scripts/build_emscripten @@ -0,0 +1,21 @@ +#! /bin/sh + +function checkprog() { + type $1 >/dev/null 2>&1 || { echo >&2 "$1 required but not found, aborting."; exit 1; } +} + +checkprog emcc +checkprog emconfigure +checkprog emmake + +# clean +emmake ./waf distclean + +# configure +emconfigure ./waf configure --prefix=$EMSCRIPTEN/system/local/ --with-target-platform emscripten + +# build +emmake ./waf --testcmd="node %s" + +# intall +#emmake ./waf install diff --git a/scripts/build_mingw b/scripts/build_mingw new file mode 100755 index 00000000..8c028943 --- /dev/null +++ b/scripts/build_mingw @@ -0,0 +1,28 @@ +#! /bin/bash + +# This script cross compiles aubio for windows using mingw, both for 32 and 64 +# bits. Built binaries will be placed in ./dist-win32 and ./dist-win64. + +# On debian or ubuntu, you will want to 'apt-get install gcc-mingw-w64' + +set -e +set -x + +WAFOPTS="-v --disable-avcodec --disable-samplerate --disable-jack --disable-sndfile" + +[ -d dist-win32 ] && rm -rf dist-win32 +[ -d dist-win64 ] && rm -rf dist-win64 + +CFLAGS="-Os" \ + LDFLAGS="" \ + CC=x86_64-w64-mingw32-gcc \ + ./waf distclean configure build install --destdir=$PWD/dist-win64 \ + --testcmd="echo %s" \ + $WAFOPTS --with-target-platform=win64 + +CFLAGS="-Os" \ + LDFLAGS="" \ + CC=i686-w64-mingw32-gcc \ + ./waf distclean configure build install --destdir=$PWD/dist-win32 \ + --testcmd="echo %s" \ + $WAFOPTS --with-target-platform=win32 diff --git a/scripts/get_waf.sh b/scripts/get_waf.sh new file mode 100755 index 00000000..5b41d8d1 --- /dev/null +++ b/scripts/get_waf.sh @@ -0,0 +1,10 @@ +#! /bin/sh + +set -e +set -x + +WAFURL=https://waf.io/waf-1.8.22 + +( which wget > /dev/null && wget -qO waf $WAFURL ) || ( which curl > /dev/null && curl $WAFURL > waf ) + +chmod +x waf diff --git a/scripts/setenv_local.sh b/scripts/setenv_local.sh new file mode 100644 index 00000000..6d33efaf --- /dev/null +++ b/scripts/setenv_local.sh @@ -0,0 +1,32 @@ +#! /usr/bin/env bash + +# This script sets the environment to execute aubio binaries and python code +# directly from build/ python/build/ without installing libaubio on the system + +# Usage: $ source ./scripts/setenv_local.sh + +# WARNING: this script will *overwrite* existing (DY)LD_LIBRARY_PATH and +# PYTHONPATH variables. + +PYTHON_PLATFORM=`python -c "import pkg_resources, sys; print ('%s-%s' % (pkg_resources.get_build_platform(), '.'.join(map(str, sys.version_info[0:2]))))"` + +AUBIODIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" +AUBIOLIB=$AUBIODIR/build/src +AUBIOPYTHON=$AUBIODIR/build/lib.$PYTHON_PLATFORM + +if [ "$(dirname $PWD)" == "scripts" ]; then + AUBIODIR=$(basename $PWD) +else + AUBIODIR=$(basename $PWD) +fi + +if [ "$(uname)" == "Darwin" ]; then + export DYLD_LIBRARY_PATH=$AUBIOLIB + echo export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH +else + export LD_LIBRARY_PATH=$AUBIOLIB + echo export LD_LIBRARY_PATH=$LD_LIBRARY_PATH +fi + +export PYTHONPATH=$AUBIOPYTHON +echo export PYTHONPATH=$PYTHONPATH diff --git a/setup.py b/setup.py new file mode 100755 index 00000000..0248a1a1 --- /dev/null +++ b/setup.py @@ -0,0 +1,82 @@ +#! /usr/bin/env python + +import sys, os.path, glob +from setuptools import setup, Extension +from python.lib.moresetuptools import * +# function to generate gen/*.{c,h} +from python.lib.gen_external import generate_external, header, output_path + +__version__ = get_aubio_pyversion() + +include_dirs = [] +library_dirs = [] +define_macros = [('AUBIO_VERSION', '%s' % __version__)] +extra_link_args = [] + +include_dirs += [ 'python/ext' ] +include_dirs += [ output_path ] # aubio-generated.h +try: + import numpy + include_dirs += [ numpy.get_include() ] +except ImportError: + pass + +if sys.platform.startswith('darwin'): + extra_link_args += ['-framework','CoreFoundation', '-framework','AudioToolbox'] + +sources = sorted(glob.glob(os.path.join('python', 'ext', '*.c'))) + +aubio_extension = Extension("aubio._aubio", + sources, + include_dirs = include_dirs, + library_dirs = library_dirs, + extra_link_args = extra_link_args, + define_macros = define_macros) + +if os.path.isfile('src/aubio.h'): + if not os.path.isdir(os.path.join('build','src')): + pass + #__version__ += 'a2' # python only version + +classifiers = [ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Science/Research', + 'Topic :: Software Development :: Libraries', + 'Topic :: Multimedia :: Sound/Audio :: Analysis', + 'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis', + 'Operating System :: POSIX', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: C', + 'Programming Language :: Python', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + ] + +distrib = setup(name='aubio', + version = __version__, + packages = ['aubio'], + package_dir = {'aubio':'python/lib/aubio'}, + scripts = ['python/scripts/aubiocut'], + ext_modules = [aubio_extension], + description = 'a collection of tools for music analysis', + long_description = 'a collection of tools for music analysis', + license = 'GNU/GPL version 3', + author = 'Paul Brossier', + author_email = 'piem@aubio.org', + maintainer = 'Paul Brossier', + maintainer_email = 'piem@aubio.org', + url = 'https://aubio.org/', + platforms = 'any', + classifiers = classifiers, + install_requires = ['numpy'], + setup_requires = ['numpy'], + cmdclass = { + 'clean': CleanGenerated, + 'build_ext': build_ext, + }, + test_suite = 'nose2.collector.collector', + extras_require = { + 'tests': ['numpy'], + }, + ) diff --git a/src/aubio.h b/src/aubio.h index cbb9d220..6a2ca4e7 100644 --- a/src/aubio.h +++ b/src/aubio.h @@ -111,6 +111,15 @@ Several examples of C programs are available in the \p examples/ and \p tests/src directories of the source tree. + Some examples: + - @ref spectral/test-fft.c + - @ref spectral/test-phasevoc.c + - @ref onset/test-onset.c + - @ref pitch/test-pitch.c + - @ref tempo/test-tempo.c + - @ref test-fvec.c + - @ref test-cvec.c + \subsection unstable_api Unstable API Several more functions are available and used within aubio, but not @@ -130,7 +139,7 @@ \section download Download Latest versions, further documentation, examples, wiki, and mailing lists can - be found at http://aubio.org . + be found at https://aubio.org . */ @@ -183,11 +192,13 @@ extern "C" #include "pitch/pitch.h" #include "onset/onset.h" #include "tempo/tempo.h" +#include "notes/notes.h" #include "io/source.h" #include "io/sink.h" #include "synth/sampler.h" #include "synth/wavetable.h" #include "utils/parameter.h" +#include "utils/log.h" #if AUBIO_UNSTABLE #include "mathutils.h" diff --git a/src/aubio_priv.h b/src/aubio_priv.h index 35b55fc7..368c5997 100644 --- a/src/aubio_priv.h +++ b/src/aubio_priv.h @@ -24,8 +24,8 @@ * This file is for inclusion from _within_ the library only. */ -#ifndef _AUBIO__PRIV_H -#define _AUBIO__PRIV_H +#ifndef AUBIO_PRIV_H +#define AUBIO_PRIV_H /********************* * @@ -33,13 +33,15 @@ * */ +#ifdef HAVE_CONFIG_H #include "config.h" +#endif -#if HAVE_STDLIB_H +#ifdef HAVE_STDLIB_H #include #endif -#if HAVE_STDIO_H +#ifdef HAVE_STDIO_H #include #endif @@ -64,10 +66,14 @@ #include // for CHAR_BIT, in C99 standard #endif +#ifdef HAVE_STDARG_H +#include +#endif + #ifdef HAVE_ACCELERATE #define HAVE_ATLAS 1 #include -#elif HAVE_ATLAS_CBLAS_H +#elif defined(HAVE_ATLAS_CBLAS_H) #define HAVE_ATLAS 1 #include #else @@ -76,7 +82,7 @@ #ifdef HAVE_ACCELERATE #include -#if !HAVE_AUBIO_DOUBLE +#ifndef HAVE_AUBIO_DOUBLE #define aubio_vDSP_mmov vDSP_mmov #define aubio_vDSP_vmul vDSP_vmul #define aubio_vDSP_vfill vDSP_vfill @@ -86,6 +92,7 @@ #define aubio_vDSP_maxvi vDSP_maxvi #define aubio_vDSP_minv vDSP_minv #define aubio_vDSP_minvi vDSP_minvi +#define aubio_vDSP_dotpr vDSP_dotpr #else /* HAVE_AUBIO_DOUBLE */ #define aubio_vDSP_mmov vDSP_mmovD #define aubio_vDSP_vmul vDSP_vmulD @@ -96,11 +103,12 @@ #define aubio_vDSP_maxvi vDSP_maxviD #define aubio_vDSP_minv vDSP_minvD #define aubio_vDSP_minvi vDSP_minviD +#define aubio_vDSP_dotpr vDSP_dotprD #endif /* HAVE_AUBIO_DOUBLE */ #endif /* HAVE_ACCELERATE */ #ifdef HAVE_ATLAS -#if !HAVE_AUBIO_DOUBLE +#ifndef HAVE_AUBIO_DOUBLE #define aubio_catlas_set catlas_sset #define aubio_cblas_copy cblas_scopy #define aubio_cblas_swap cblas_sswap @@ -166,16 +174,25 @@ typedef enum { AUBIO_FAIL = 1 } aubio_status; +/* Logging */ + +#include "utils/log.h" + +/** internal logging function, defined in utils/log.c */ +uint_t aubio_log(sint_t level, const char_t *fmt, ...); + #ifdef HAVE_C99_VARARGS_MACROS -#define AUBIO_ERR(...) fprintf(stderr, "AUBIO ERROR: " __VA_ARGS__) -#define AUBIO_MSG(...) fprintf(stdout, __VA_ARGS__) -#define AUBIO_DBG(...) fprintf(stderr, __VA_ARGS__) -#define AUBIO_WRN(...) fprintf(stderr, "AUBIO WARNING: " __VA_ARGS__) +#define AUBIO_ERR(...) aubio_log(AUBIO_LOG_ERR, "AUBIO ERROR: " __VA_ARGS__) +#define AUBIO_INF(...) aubio_log(AUBIO_LOG_INF, "AUBIO INFO: " __VA_ARGS__) +#define AUBIO_MSG(...) aubio_log(AUBIO_LOG_MSG, __VA_ARGS__) +#define AUBIO_DBG(...) aubio_log(AUBIO_LOG_DBG, __VA_ARGS__) +#define AUBIO_WRN(...) aubio_log(AUBIO_LOG_WRN, "AUBIO WARNING: " __VA_ARGS__) #else -#define AUBIO_ERR(format, args...) fprintf(stderr, "AUBIO ERROR: " format , ##args) -#define AUBIO_MSG(format, args...) fprintf(stdout, format , ##args) -#define AUBIO_DBG(format, args...) fprintf(stderr, format , ##args) -#define AUBIO_WRN(format, args...) fprintf(stderr, "AUBIO WARNING: " format, ##args) +#define AUBIO_ERR(format, args...) aubio_log(AUBIO_LOG_ERR, "AUBIO ERROR: " format , ##args) +#define AUBIO_INF(format, args...) aubio_log(AUBIO_LOG_INF, "AUBIO INFO: " format , ##args) +#define AUBIO_MSG(format, args...) aubio_log(AUBIO_LOG_MSG, format , ##args) +#define AUBIO_DBG(format, args...) aubio_log(AUBIO_LOG_DBG, format , ##args) +#define AUBIO_WRN(format, args...) aubio_log(AUBIO_LOG_WRN, "AUBIO WARNING: " format, ##args) #endif #define AUBIO_ERROR AUBIO_ERR @@ -183,6 +200,9 @@ typedef enum { #define AUBIO_QUIT(_s) exit(_s) #define AUBIO_SPRINTF sprintf +#define AUBIO_MAX_SAMPLERATE (192000*8) +#define AUBIO_MAX_CHANNELS 1024 + /* pi and 2*pi */ #ifndef M_PI #define PI (3.14159265358979323846) @@ -191,6 +211,10 @@ typedef enum { #endif #define TWO_PI (PI*2.) +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + /* aliases to math.h functions */ #if !HAVE_AUBIO_DOUBLE #define EXP expf @@ -203,6 +227,7 @@ typedef enum { #define LOG logf #define FLOOR floorf #define CEIL ceilf +#define ATAN atanf #define ATAN2 atan2f #else #define EXP exp @@ -215,6 +240,7 @@ typedef enum { #define LOG log #define FLOOR floor #define CEIL ceil +#define ATAN atan #define ATAN2 atan2 #endif #define ROUND(x) FLOOR(x+.5) @@ -248,6 +274,11 @@ typedef enum { #define IMAG cimagf #endif +/* avoid unresolved symbol with msvc 9 */ +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define isnan _isnan +#endif + /* handy shortcuts */ #define DB2LIN(g) (POW(10.0,(g)*0.05f)) #define LIN2DB(v) (20.0*LOG10(v)) @@ -283,4 +314,12 @@ typedef enum { #define UNUSED #endif -#endif /* _AUBIO__PRIV_H */ +/* are we using gcc -std=c99 ? */ +#if defined(__STRICT_ANSI__) +#define strnlen(a,b) MIN(strlen(a),b) +#if !HAVE_AUBIO_DOUBLE +#define floorf floor +#endif +#endif /* __STRICT_ANSI__ */ + +#endif /* AUBIO_PRIV_H */ diff --git a/src/cvec.c b/src/cvec.c index 6d1edb83..2e98cf5f 100644 --- a/src/cvec.c +++ b/src/cvec.c @@ -21,7 +21,7 @@ #include "aubio_priv.h" #include "cvec.h" -cvec_t * new_cvec( uint_t length) { +cvec_t * new_cvec(uint_t length) { cvec_t * s; if ((sint_t)length <= 0) { return NULL; @@ -55,17 +55,17 @@ smpl_t cvec_phas_get_sample (cvec_t *s, uint_t position) { return s->phas[position]; } -smpl_t * cvec_norm_get_data (cvec_t *s) { +smpl_t * cvec_norm_get_data (const cvec_t *s) { return s->norm; } -smpl_t * cvec_phas_get_data (cvec_t *s) { +smpl_t * cvec_phas_get_data (const cvec_t *s) { return s->phas; } /* helper functions */ -void cvec_print(cvec_t *s) { +void cvec_print(const cvec_t *s) { uint_t j; AUBIO_MSG("norm: "); for (j=0; j< s->length; j++) { @@ -79,22 +79,22 @@ void cvec_print(cvec_t *s) { AUBIO_MSG("\n"); } -void cvec_copy(cvec_t *s, cvec_t *t) { +void cvec_copy(const cvec_t *s, cvec_t *t) { if (s->length != t->length) { AUBIO_ERR("trying to copy %d elements to %d elements \n", s->length, t->length); return; } -#if HAVE_MEMCPY_HACKS +#ifdef HAVE_MEMCPY_HACKS memcpy(t->norm, s->norm, t->length * sizeof(smpl_t)); memcpy(t->phas, s->phas, t->length * sizeof(smpl_t)); -#else +#else /* HAVE_MEMCPY_HACKS */ uint_t j; for (j=0; j< t->length; j++) { t->norm[j] = s->norm[j]; t->phas[j] = s->phas[j]; } -#endif +#endif /* HAVE_MEMCPY_HACKS */ } void cvec_norm_set_all (cvec_t *s, smpl_t val) { @@ -105,11 +105,11 @@ void cvec_norm_set_all (cvec_t *s, smpl_t val) { } void cvec_norm_zeros(cvec_t *s) { -#if HAVE_MEMCPY_HACKS +#ifdef HAVE_MEMCPY_HACKS memset(s->norm, 0, s->length * sizeof(smpl_t)); -#else +#else /* HAVE_MEMCPY_HACKS */ cvec_norm_set_all (s, 0.); -#endif +#endif /* HAVE_MEMCPY_HACKS */ } void cvec_norm_ones(cvec_t *s) { @@ -124,7 +124,7 @@ void cvec_phas_set_all (cvec_t *s, smpl_t val) { } void cvec_phas_zeros(cvec_t *s) { -#if HAVE_MEMCPY_HACKS +#ifdef HAVE_MEMCPY_HACKS memset(s->phas, 0, s->length * sizeof(smpl_t)); #else cvec_phas_set_all (s, 0.); diff --git a/src/cvec.h b/src/cvec.h index c77853ff..d0a9c2ba 100644 --- a/src/cvec.h +++ b/src/cvec.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO__CVEC_H -#define _AUBIO__CVEC_H +#ifndef AUBIO_CVEC_H +#define AUBIO_CVEC_H #ifdef __cplusplus extern "C" { @@ -150,7 +150,7 @@ smpl_t cvec_phas_get_sample (cvec_t *s, uint_t position); \param s vector to read from */ -smpl_t * cvec_norm_get_data (cvec_t *s); +smpl_t * cvec_norm_get_data (const cvec_t *s); /** read phase data from a complex buffer @@ -162,14 +162,14 @@ smpl_t * cvec_norm_get_data (cvec_t *s); \param s vector to read from */ -smpl_t * cvec_phas_get_data (cvec_t *s); +smpl_t * cvec_phas_get_data (const cvec_t *s); /** print out cvec data \param s vector to print out */ -void cvec_print(cvec_t *s); +void cvec_print(const cvec_t *s); /** make a copy of a vector @@ -177,7 +177,7 @@ void cvec_print(cvec_t *s); \param t vector to copy to */ -void cvec_copy(cvec_t *s, cvec_t *t); +void cvec_copy(const cvec_t *s, cvec_t *t); /** set all norm elements to a given value @@ -234,4 +234,4 @@ void cvec_zeros(cvec_t *s); } #endif -#endif /* _AUBIO__CVEC_H */ +#endif /* AUBIO_CVEC_H */ diff --git a/src/fmat.c b/src/fmat.c index a51c18a9..c8541a52 100644 --- a/src/fmat.c +++ b/src/fmat.c @@ -53,27 +53,27 @@ void fmat_set_sample(fmat_t *s, smpl_t data, uint_t channel, uint_t position) { s->data[channel][position] = data; } -smpl_t fmat_get_sample(fmat_t *s, uint_t channel, uint_t position) { +smpl_t fmat_get_sample(const fmat_t *s, uint_t channel, uint_t position) { return s->data[channel][position]; } -void fmat_get_channel(fmat_t *s, uint_t channel, fvec_t *output) { +void fmat_get_channel(const fmat_t *s, uint_t channel, fvec_t *output) { output->data = s->data[channel]; output->length = s->length; return; } -smpl_t * fmat_get_channel_data(fmat_t *s, uint_t channel) { +smpl_t * fmat_get_channel_data(const fmat_t *s, uint_t channel) { return s->data[channel]; } -smpl_t ** fmat_get_data(fmat_t *s) { +smpl_t ** fmat_get_data(const fmat_t *s) { return s->data; } /* helper functions */ -void fmat_print(fmat_t *s) { +void fmat_print(const fmat_t *s) { uint_t i,j; for (i=0; i< s->height; i++) { for (j=0; j< s->length; j++) { @@ -93,14 +93,14 @@ void fmat_set(fmat_t *s, smpl_t val) { } void fmat_zeros(fmat_t *s) { -#if HAVE_MEMCPY_HACKS +#ifdef HAVE_MEMCPY_HACKS uint_t i; for (i=0; i< s->height; i++) { memset(s->data[i], 0, s->length * sizeof(smpl_t)); } -#else +#else /* HAVE_MEMCPY_HACKS */ fmat_set(s, 0.); -#endif +#endif /* HAVE_MEMCPY_HACKS */ } void fmat_ones(fmat_t *s) { @@ -110,13 +110,13 @@ void fmat_ones(fmat_t *s) { void fmat_rev(fmat_t *s) { uint_t i,j; for (i=0; i< s->height; i++) { - for (j=0; j< FLOOR(s->length/2); j++) { + for (j=0; j< FLOOR((smpl_t)s->length/2); j++) { ELEM_SWAP(s->data[i][j], s->data[i][s->length-1-j]); } } } -void fmat_weight(fmat_t *s, fmat_t *weight) { +void fmat_weight(fmat_t *s, const fmat_t *weight) { uint_t i,j; uint_t length = MIN(s->length, weight->length); for (i=0; i< s->height; i++) { @@ -126,11 +126,11 @@ void fmat_weight(fmat_t *s, fmat_t *weight) { } } -void fmat_copy(fmat_t *s, fmat_t *t) { +void fmat_copy(const fmat_t *s, fmat_t *t) { uint_t i; -#if !HAVE_MEMCPY_HACKS +#ifndef HAVE_MEMCPY_HACKS uint_t j; -#endif +#endif /* HAVE_MEMCPY_HACKS */ if (s->height != t->height) { AUBIO_ERR("trying to copy %d rows to %d rows \n", s->height, t->height); @@ -141,16 +141,46 @@ void fmat_copy(fmat_t *s, fmat_t *t) { s->length, t->length); return; } -#if HAVE_MEMCPY_HACKS +#ifdef HAVE_MEMCPY_HACKS for (i=0; i< s->height; i++) { memcpy(t->data[i], s->data[i], t->length * sizeof(smpl_t)); } -#else +#else /* HAVE_MEMCPY_HACKS */ for (i=0; i< t->height; i++) { for (j=0; j< t->length; j++) { t->data[i][j] = s->data[i][j]; } } -#endif +#endif /* HAVE_MEMCPY_HACKS */ } +void fmat_vecmul(const fmat_t *s, const fvec_t *scale, fvec_t *output) { + uint_t k; +#if 0 + assert(s->height == output->length); + assert(s->length == scale->length); +#endif +#if !defined(HAVE_ACCELERATE) && !defined(HAVE_ATLAS) + uint_t j; + fvec_zeros(output); + for (j = 0; j < s->length; j++) { + for (k = 0; k < s->height; k++) { + output->data[k] += scale->data[j] + * s->data[k][j]; + } + } +#elif defined(HAVE_ATLAS) + for (k = 0; k < s->height; k++) { + output->data[k] = aubio_cblas_dot( s->length, scale->data, 1, s->data[k], 1); + } +#elif defined(HAVE_ACCELERATE) +#if 0 + // seems slower and less precise (and dangerous?) + vDSP_mmul (s->data[0], 1, scale->data, 1, output->data, 1, s->height, 1, s->length); +#else + for (k = 0; k < s->height; k++) { + aubio_vDSP_dotpr( scale->data, 1, s->data[k], 1, &(output->data[k]), s->length); + } +#endif +#endif +} diff --git a/src/fmat.h b/src/fmat.h index f844832b..8e65d110 100644 --- a/src/fmat.h +++ b/src/fmat.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO__FMAT_H -#define _AUBIO__FMAT_H +#ifndef AUBIO_FMAT_H +#define AUBIO_FMAT_H #ifdef __cplusplus extern "C" { @@ -65,7 +65,7 @@ void del_fmat(fmat_t *s); \param position sample position to read from */ -smpl_t fmat_get_sample(fmat_t *s, uint_t channel, uint_t position); +smpl_t fmat_get_sample(const fmat_t *s, uint_t channel, uint_t position); /** write sample value in a buffer @@ -84,7 +84,7 @@ void fmat_set_sample(fmat_t *s, smpl_t data, uint_t channel, uint_t position); \param output ::fvec_t to output to */ -void fmat_get_channel (fmat_t *s, uint_t channel, fvec_t *output); +void fmat_get_channel (const fmat_t *s, uint_t channel, fvec_t *output); /** get vector buffer from an fmat data @@ -92,21 +92,21 @@ void fmat_get_channel (fmat_t *s, uint_t channel, fvec_t *output); \param channel channel to read from */ -smpl_t * fmat_get_channel_data (fmat_t *s, uint_t channel); +smpl_t * fmat_get_channel_data (const fmat_t *s, uint_t channel); /** read data from a buffer \param s vector to read from */ -smpl_t ** fmat_get_data(fmat_t *s); +smpl_t ** fmat_get_data(const fmat_t *s); /** print out fmat data \param s vector to print out */ -void fmat_print(fmat_t *s); +void fmat_print(const fmat_t *s); /** set all elements to a given value @@ -146,7 +146,7 @@ void fmat_rev(fmat_t *s); \param weight weighting coefficients */ -void fmat_weight(fmat_t *s, fmat_t *weight); +void fmat_weight(fmat_t *s, const fmat_t *weight); /** make a copy of a matrix @@ -154,10 +154,19 @@ void fmat_weight(fmat_t *s, fmat_t *weight); \param t vector to copy to */ -void fmat_copy(fmat_t *s, fmat_t *t); +void fmat_copy(const fmat_t *s, fmat_t *t); + +/** compute the product of a matrix by a vector + + \param s matrix to compute product with + \param scale vector to compute product with + \param output vector to store restults in + +*/ +void fmat_vecmul(const fmat_t *s, const fvec_t *scale, fvec_t *output); #ifdef __cplusplus } #endif -#endif /* _AUBIO__FMAT_H */ +#endif /* AUBIO_FMAT_H */ diff --git a/src/fvec.c b/src/fvec.c index 1b0f750e..3961d746 100644 --- a/src/fvec.c +++ b/src/fvec.c @@ -21,7 +21,7 @@ #include "aubio_priv.h" #include "fvec.h" -fvec_t * new_fvec( uint_t length) { +fvec_t * new_fvec(uint_t length) { fvec_t * s; if ((sint_t)length <= 0) { return NULL; @@ -41,17 +41,17 @@ void fvec_set_sample(fvec_t *s, smpl_t data, uint_t position) { s->data[position] = data; } -smpl_t fvec_get_sample(fvec_t *s, uint_t position) { +smpl_t fvec_get_sample(const fvec_t *s, uint_t position) { return s->data[position]; } -smpl_t * fvec_get_data(fvec_t *s) { +smpl_t * fvec_get_data(const fvec_t *s) { return s->data; } /* helper functions */ -void fvec_print(fvec_t *s) { +void fvec_print(const fvec_t *s) { uint_t j; for (j=0; j< s->length; j++) { AUBIO_MSG(AUBIO_SMPL_FMT " ", s->data[j]); @@ -90,12 +90,12 @@ void fvec_ones(fvec_t *s) { void fvec_rev(fvec_t *s) { uint_t j; - for (j=0; j< FLOOR(s->length/2); j++) { + for (j=0; j< FLOOR((smpl_t)s->length/2); j++) { ELEM_SWAP(s->data[j], s->data[s->length-1-j]); } } -void fvec_weight(fvec_t *s, fvec_t *weight) { +void fvec_weight(fvec_t *s, const fvec_t *weight) { #ifndef HAVE_ACCELERATE uint_t j; uint_t length = MIN(s->length, weight->length); @@ -107,7 +107,7 @@ void fvec_weight(fvec_t *s, fvec_t *weight) { #endif /* HAVE_ACCELERATE */ } -void fvec_weighted_copy(fvec_t *in, fvec_t *weight, fvec_t *out) { +void fvec_weighted_copy(const fvec_t *in, const fvec_t *weight, fvec_t *out) { #ifndef HAVE_ACCELERATE uint_t j; uint_t length = MIN(out->length, weight->length); @@ -119,13 +119,13 @@ void fvec_weighted_copy(fvec_t *in, fvec_t *weight, fvec_t *out) { #endif /* HAVE_ACCELERATE */ } -void fvec_copy(fvec_t *s, fvec_t *t) { +void fvec_copy(const fvec_t *s, fvec_t *t) { if (s->length != t->length) { AUBIO_ERR("trying to copy %d elements to %d elements \n", s->length, t->length); return; } -#if HAVE_NOOPT +#ifdef HAVE_NOOPT uint_t j; for (j=0; j< t->length; j++) { t->data[j] = s->data[j]; diff --git a/src/fvec.h b/src/fvec.h index ceaec224..bd8c5a60 100644 --- a/src/fvec.h +++ b/src/fvec.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO__FVEC_H -#define _AUBIO__FVEC_H +#ifndef AUBIO_FVEC_H +#define AUBIO_FVEC_H #ifdef __cplusplus extern "C" { @@ -89,7 +89,7 @@ void del_fvec(fvec_t *s); \param position sample position to read from */ -smpl_t fvec_get_sample(fvec_t *s, uint_t position); +smpl_t fvec_get_sample(const fvec_t *s, uint_t position); /** write sample value in a buffer @@ -105,14 +105,14 @@ void fvec_set_sample(fvec_t *s, smpl_t data, uint_t position); \param s vector to read from */ -smpl_t * fvec_get_data(fvec_t *s); +smpl_t * fvec_get_data(const fvec_t *s); /** print out fvec data \param s vector to print out */ -void fvec_print(fvec_t *s); +void fvec_print(const fvec_t *s); /** set all elements to a given value @@ -152,7 +152,7 @@ void fvec_rev(fvec_t *s); \param weight weighting coefficients */ -void fvec_weight(fvec_t *s, fvec_t *weight); +void fvec_weight(fvec_t *s, const fvec_t *weight); /** make a copy of a vector @@ -160,7 +160,7 @@ void fvec_weight(fvec_t *s, fvec_t *weight); \param t vector to copy to */ -void fvec_copy(fvec_t *s, fvec_t *t); +void fvec_copy(const fvec_t *s, fvec_t *t); /** make a copy of a vector, applying weights to each element @@ -169,10 +169,10 @@ void fvec_copy(fvec_t *s, fvec_t *t); \param out output vector */ -void fvec_weighted_copy(fvec_t *in, fvec_t *weight, fvec_t *out); +void fvec_weighted_copy(const fvec_t *in, const fvec_t *weight, fvec_t *out); #ifdef __cplusplus } #endif -#endif /* _AUBIO__FVEC_H */ +#endif /* AUBIO_FVEC_H */ diff --git a/src/io/audio_unit.c b/src/io/audio_unit.c index a21906ae..2674388a 100644 --- a/src/io/audio_unit.c +++ b/src/io/audio_unit.c @@ -18,9 +18,8 @@ */ -#include "config.h" -#ifdef HAVE_AUDIO_UNIT #include "aubio_priv.h" +#ifdef HAVE_AUDIO_UNIT #include "fvec.h" #include "fmat.h" diff --git a/src/io/audio_unit.h b/src/io/audio_unit.h index c518bc64..46f86b1e 100644 --- a/src/io/audio_unit.h +++ b/src/io/audio_unit.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_AUDIO_UNIT_H -#define _AUBIO_AUDIO_UNIT_H +#ifndef AUBIO_AUDIO_UNIT_H +#define AUBIO_AUDIO_UNIT_H /** \file @@ -58,4 +58,4 @@ uint_t del_aubio_audio_unit(aubio_audio_unit_t *o); } #endif -#endif /* _AUBIO_AUDIO_UNIT_H */ +#endif /* AUBIO_AUDIO_UNIT_H */ diff --git a/src/io/ioutils.c b/src/io/ioutils.c new file mode 100644 index 00000000..cce93c67 --- /dev/null +++ b/src/io/ioutils.c @@ -0,0 +1,53 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +#include "aubio_priv.h" + +uint_t +aubio_io_validate_samplerate(const char_t *kind, const char_t *path, uint_t samplerate) +{ + if ((sint_t)(samplerate) <= 0) { + AUBIO_ERR("%s: failed creating %s, samplerate should be positive, not %d\n", + kind, path, samplerate); + return AUBIO_FAIL; + } + if ((sint_t)samplerate > AUBIO_MAX_SAMPLERATE) { + AUBIO_ERR("%s: failed creating %s, samplerate %dHz is too large\n", + kind, path, samplerate); + return AUBIO_FAIL; + } + return AUBIO_OK; +} + +uint_t +aubio_io_validate_channels(const char_t *kind, const char_t *path, uint_t channels) +{ + if ((sint_t)(channels) <= 0) { + AUBIO_ERR("sink_%s: failed creating %s, channels should be positive, not %d\n", + kind, path, channels); + return AUBIO_FAIL; + } + if (channels > AUBIO_MAX_CHANNELS) { + AUBIO_ERR("sink_%s: failed creating %s, too many channels (%d but %d available)\n", + kind, path, channels, AUBIO_MAX_CHANNELS); + return AUBIO_FAIL; + } + return AUBIO_OK; +} diff --git a/src/io/ioutils.h b/src/io/ioutils.h new file mode 100644 index 00000000..7eb65cbc --- /dev/null +++ b/src/io/ioutils.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +#ifndef AUBIO_IOUTILS_H +#define AUBIO_IOUTILS_H + +/** \file + + Simple utility functions to validate input parameters. + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** validate samplerate + + \param kind the object kind to report on + \param path the object properties to report on + \param samplerate the object properties to report on + \return 0 if ok, non-zero if validation failed + + */ +uint_t aubio_io_validate_samplerate(const char_t *kind, const char_t *path, + uint_t samplerate); + +/** validate number of channels + + \param kind the object kind to report on + \param path the object properties to report on + \param channels the object properties to report on + \return 0 if ok, non-zero if validation failed + + */ +uint_t aubio_io_validate_channels(const char_t *kind, const char_t *path, + uint_t channels); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_IOUTILS_H */ diff --git a/src/io/sink.c b/src/io/sink.c index 76980bb8..70383cc7 100644 --- a/src/io/sink.c +++ b/src/io/sink.c @@ -18,7 +18,6 @@ */ -#include "config.h" #include "aubio_priv.h" #include "fvec.h" #include "fmat.h" @@ -54,7 +53,7 @@ struct _aubio_sink_t { del_aubio_sink_t s_del; }; -aubio_sink_t * new_aubio_sink(char_t * uri, uint_t samplerate) { +aubio_sink_t * new_aubio_sink(const char_t * uri, uint_t samplerate) { aubio_sink_t * s = AUBIO_NEW(aubio_sink_t); #ifdef HAVE_SINK_APPLE_AUDIO s->sink = (void *)new_aubio_sink_apple_audio(uri, samplerate); @@ -70,7 +69,7 @@ aubio_sink_t * new_aubio_sink(char_t * uri, uint_t samplerate) { return s; } #endif /* HAVE_SINK_APPLE_AUDIO */ -#if HAVE_SNDFILE +#ifdef HAVE_SNDFILE s->sink = (void *)new_aubio_sink_sndfile(uri, samplerate); if (s->sink) { s->s_do = (aubio_sink_do_t)(aubio_sink_sndfile_do); @@ -84,7 +83,7 @@ aubio_sink_t * new_aubio_sink(char_t * uri, uint_t samplerate) { return s; } #endif /* HAVE_SNDFILE */ -#if HAVE_WAVWRITE +#ifdef HAVE_WAVWRITE s->sink = (void *)new_aubio_sink_wavwrite(uri, samplerate); if (s->sink) { s->s_do = (aubio_sink_do_t)(aubio_sink_wavwrite_do); @@ -98,8 +97,11 @@ aubio_sink_t * new_aubio_sink(char_t * uri, uint_t samplerate) { return s; } #endif /* HAVE_WAVWRITE */ - AUBIO_ERROR("sink: failed creating %s with samplerate %dHz\n", - uri, samplerate); +#if !defined(HAVE_WAVWRITE) && \ + !defined(HAVE_SNDFILE) && \ + !defined(HAVE_SINK_APPLE_AUDIO) + AUBIO_ERROR("sink: failed creating '%s' at %dHz (no sink built-in)\n", uri, samplerate); +#endif AUBIO_FREE(s); return NULL; } @@ -120,11 +122,11 @@ uint_t aubio_sink_preset_channels(aubio_sink_t * s, uint_t channels) { return s->s_preset_channels((void *)s->sink, channels); } -uint_t aubio_sink_get_samplerate(aubio_sink_t * s) { +uint_t aubio_sink_get_samplerate(const aubio_sink_t * s) { return s->s_get_samplerate((void *)s->sink); } -uint_t aubio_sink_get_channels(aubio_sink_t * s) { +uint_t aubio_sink_get_channels(const aubio_sink_t * s) { return s->s_get_channels((void *)s->sink); } diff --git a/src/io/sink.h b/src/io/sink.h index 954bd807..ee0f5cf8 100644 --- a/src/io/sink.h +++ b/src/io/sink.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SINK_H -#define _AUBIO_SINK_H +#ifndef AUBIO_SINK_H +#define AUBIO_SINK_H /** \file @@ -76,7 +76,7 @@ typedef struct _aubio_sink_t aubio_sink_t; been called. */ -aubio_sink_t * new_aubio_sink(char_t * uri, uint_t samplerate); +aubio_sink_t * new_aubio_sink(const char_t * uri, uint_t samplerate); /** @@ -120,7 +120,7 @@ uint_t aubio_sink_preset_channels(aubio_sink_t *s, uint_t channels); \return samplerate, in Hz */ -uint_t aubio_sink_get_samplerate(aubio_sink_t *s); +uint_t aubio_sink_get_samplerate(const aubio_sink_t *s); /** @@ -130,7 +130,7 @@ uint_t aubio_sink_get_samplerate(aubio_sink_t *s); \return number of channels */ -uint_t aubio_sink_get_channels(aubio_sink_t *s); +uint_t aubio_sink_get_channels(const aubio_sink_t *s); /** @@ -178,4 +178,4 @@ void del_aubio_sink(aubio_sink_t * s); } #endif -#endif /* _AUBIO_SINK_H */ +#endif /* AUBIO_SINK_H */ diff --git a/src/io/sink_apple_audio.c b/src/io/sink_apple_audio.c index d4929520..32fbbf45 100644 --- a/src/io/sink_apple_audio.c +++ b/src/io/sink_apple_audio.c @@ -18,14 +18,13 @@ */ -#include "config.h" +#include "aubio_priv.h" #ifdef HAVE_SINK_APPLE_AUDIO - -#include "aubio_priv.h" #include "fvec.h" #include "fmat.h" #include "io/sink_apple_audio.h" +#include "io/ioutils.h" // CFURLRef, CFURLCreateWithFileSystemPath, ... #include @@ -43,6 +42,8 @@ uint_t aubio_sink_apple_audio_open(aubio_sink_apple_audio_t *s); #define MAX_SIZE 4096 // the maximum number of frames that can be written at a time +void aubio_sink_apple_audio_write(aubio_sink_apple_audio_t *s, uint_t write); + struct _aubio_sink_apple_audio_t { uint_t samplerate; uint_t channels; @@ -55,24 +56,30 @@ struct _aubio_sink_apple_audio_t { bool async; }; -aubio_sink_apple_audio_t * new_aubio_sink_apple_audio(char_t * uri, uint_t samplerate) { +aubio_sink_apple_audio_t * new_aubio_sink_apple_audio(const char_t * uri, uint_t samplerate) { aubio_sink_apple_audio_t * s = AUBIO_NEW(aubio_sink_apple_audio_t); - s->path = uri; s->max_frames = MAX_SIZE; - s->async = true; + s->async = false; - if (uri == NULL) { + if ( (uri == NULL) || (strlen(uri) < 1) ) { AUBIO_ERROR("sink_apple_audio: Aborted opening null path\n"); goto beach; } + if (s->path != NULL) AUBIO_FREE(s->path); + s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1); + strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1); s->samplerate = 0; s->channels = 0; - // negative samplerate given, abort - if ((sint_t)samplerate < 0) goto beach; // zero samplerate given. do not open yet - if ((sint_t)samplerate == 0) return s; + if ((sint_t)samplerate == 0) { + return s; + } + // invalid samplerate given, abort + if (aubio_io_validate_samplerate("sink_apple_audio", s->path, samplerate)) { + goto beach; + } s->samplerate = samplerate; s->channels = 1; @@ -90,7 +97,9 @@ beach: uint_t aubio_sink_apple_audio_preset_samplerate(aubio_sink_apple_audio_t *s, uint_t samplerate) { - if ((sint_t)(samplerate) <= 0) return AUBIO_FAIL; + if (aubio_io_validate_samplerate("sink_apple_audio", s->path, samplerate)) { + return AUBIO_FAIL; + } s->samplerate = samplerate; // automatically open when both samplerate and channels have been set if (s->samplerate != 0 && s->channels != 0) { @@ -101,7 +110,9 @@ uint_t aubio_sink_apple_audio_preset_samplerate(aubio_sink_apple_audio_t *s, uin uint_t aubio_sink_apple_audio_preset_channels(aubio_sink_apple_audio_t *s, uint_t channels) { - if ((sint_t)(channels) <= 0) return AUBIO_FAIL; + if (aubio_io_validate_channels("sink_apple_audio", s->path, channels)) { + return AUBIO_FAIL; + } s->channels = channels; // automatically open when both samplerate and channels have been set if (s->samplerate != 0 && s->channels != 0) { @@ -110,12 +121,12 @@ uint_t aubio_sink_apple_audio_preset_channels(aubio_sink_apple_audio_t *s, uint_ return AUBIO_OK; } -uint_t aubio_sink_apple_audio_get_samplerate(aubio_sink_apple_audio_t *s) +uint_t aubio_sink_apple_audio_get_samplerate(const aubio_sink_apple_audio_t *s) { return s->samplerate; } -uint_t aubio_sink_apple_audio_get_channels(aubio_sink_apple_audio_t *s) +uint_t aubio_sink_apple_audio_get_channels(const aubio_sink_apple_audio_t *s) { return s->channels; } @@ -162,7 +173,6 @@ beach: } void aubio_sink_apple_audio_do(aubio_sink_apple_audio_t * s, fvec_t * write_data, uint_t write) { - OSStatus err = noErr; UInt32 c, v; short *data = (short*)s->bufferList.mBuffers[0].mData; if (write > s->max_frames) { @@ -179,34 +189,10 @@ void aubio_sink_apple_audio_do(aubio_sink_apple_audio_t * s, fvec_t * write_data } } } - if (s->async) { - err = ExtAudioFileWriteAsync(s->audioFile, write, &s->bufferList); - - if (err) { - char_t errorstr[20]; - AUBIO_ERROR("sink_apple_audio: error while writing %s " - "in ExtAudioFileWriteAsync (%s), switching to sync\n", s->path, - getPrintableOSStatusError(errorstr, err)); - s->async = false; - } else { - return; - } - - } else { - err = ExtAudioFileWrite(s->audioFile, write, &s->bufferList); - - if (err) { - char_t errorstr[20]; - AUBIO_ERROR("sink_apple_audio: error while writing %s " - "in ExtAudioFileWrite (%s)\n", s->path, - getPrintableOSStatusError(errorstr, err)); - } - } - return; + aubio_sink_apple_audio_write(s, write); } void aubio_sink_apple_audio_do_multi(aubio_sink_apple_audio_t * s, fmat_t * write_data, uint_t write) { - OSStatus err = noErr; UInt32 c, v; short *data = (short*)s->bufferList.mBuffers[0].mData; if (write > s->max_frames) { @@ -223,22 +209,28 @@ void aubio_sink_apple_audio_do_multi(aubio_sink_apple_audio_t * s, fmat_t * writ } } } + aubio_sink_apple_audio_write(s, write); +} + +void aubio_sink_apple_audio_write(aubio_sink_apple_audio_t *s, uint_t write) { + OSStatus err = noErr; if (s->async) { err = ExtAudioFileWriteAsync(s->audioFile, write, &s->bufferList); - if (err) { char_t errorstr[20]; + if (err == kExtAudioFileError_AsyncWriteBufferOverflow) { + sprintf(errorstr,"buffer overflow"); + } else if (err == kExtAudioFileError_AsyncWriteTooLarge) { + sprintf(errorstr,"write too large"); + } else { + // unknown error + getPrintableOSStatusError(errorstr, err); + } AUBIO_ERROR("sink_apple_audio: error while writing %s " - "in ExtAudioFileWriteAsync (%s), switching to sync\n", s->path, - getPrintableOSStatusError(errorstr, err)); - s->async = false; - } else { - return; + "in ExtAudioFileWriteAsync (%s)\n", s->path, errorstr); } - } else { err = ExtAudioFileWrite(s->audioFile, write, &s->bufferList); - if (err) { char_t errorstr[20]; AUBIO_ERROR("sink_apple_audio: error while writing %s " @@ -246,7 +238,6 @@ void aubio_sink_apple_audio_do_multi(aubio_sink_apple_audio_t * s, fmat_t * writ getPrintableOSStatusError(errorstr, err)); } } - return; } uint_t aubio_sink_apple_audio_close(aubio_sink_apple_audio_t * s) { @@ -267,6 +258,7 @@ uint_t aubio_sink_apple_audio_close(aubio_sink_apple_audio_t * s) { void del_aubio_sink_apple_audio(aubio_sink_apple_audio_t * s) { if (s->audioFile) aubio_sink_apple_audio_close (s); + if (s->path) AUBIO_FREE(s->path); freeAudioBufferList(&s->bufferList); AUBIO_FREE(s); return; diff --git a/src/io/sink_apple_audio.h b/src/io/sink_apple_audio.h index b9efe97f..14d6a8aa 100644 --- a/src/io/sink_apple_audio.h +++ b/src/io/sink_apple_audio.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SINK_APPLE_AUDIO_H -#define _AUBIO_SINK_APPLE_AUDIO_H +#ifndef AUBIO_SINK_APPLE_AUDIO_H +#define AUBIO_SINK_APPLE_AUDIO_H /** \file @@ -58,7 +58,7 @@ typedef struct _aubio_sink_apple_audio_t aubio_sink_apple_audio_t; been called. */ -aubio_sink_apple_audio_t * new_aubio_sink_apple_audio(char_t * uri, uint_t samplerate); +aubio_sink_apple_audio_t * new_aubio_sink_apple_audio(const char_t * uri, uint_t samplerate); /** @@ -102,7 +102,7 @@ uint_t aubio_sink_apple_audio_preset_channels(aubio_sink_apple_audio_t *s, uint_ \return samplerate, in Hz */ -uint_t aubio_sink_apple_audio_get_samplerate(aubio_sink_apple_audio_t *s); +uint_t aubio_sink_apple_audio_get_samplerate(const aubio_sink_apple_audio_t *s); /** @@ -112,7 +112,7 @@ uint_t aubio_sink_apple_audio_get_samplerate(aubio_sink_apple_audio_t *s); \return number of channels */ -uint_t aubio_sink_apple_audio_get_channels(aubio_sink_apple_audio_t *s); +uint_t aubio_sink_apple_audio_get_channels(const aubio_sink_apple_audio_t *s); /** @@ -160,4 +160,4 @@ void del_aubio_sink_apple_audio(aubio_sink_apple_audio_t * s); } #endif -#endif /* _AUBIO_SINK_APPLE_AUDIO_H */ +#endif /* AUBIO_SINK_APPLE_AUDIO_H */ diff --git a/src/io/sink_sndfile.c b/src/io/sink_sndfile.c index 9fed7606..c105221e 100644 --- a/src/io/sink_sndfile.c +++ b/src/io/sink_sndfile.c @@ -19,18 +19,17 @@ */ -#include "config.h" +#include "aubio_priv.h" #ifdef HAVE_SNDFILE #include -#include "aubio_priv.h" #include "fvec.h" #include "fmat.h" #include "io/sink_sndfile.h" +#include "io/ioutils.h" -#define MAX_CHANNELS 6 #define MAX_SIZE 4096 #if !HAVE_AUBIO_DOUBLE @@ -53,23 +52,30 @@ struct _aubio_sink_sndfile_t { uint_t aubio_sink_sndfile_open(aubio_sink_sndfile_t *s); -aubio_sink_sndfile_t * new_aubio_sink_sndfile(char_t * path, uint_t samplerate) { +aubio_sink_sndfile_t * new_aubio_sink_sndfile(const char_t * path, uint_t samplerate) { aubio_sink_sndfile_t * s = AUBIO_NEW(aubio_sink_sndfile_t); s->max_size = MAX_SIZE; - s->path = path; if (path == NULL) { AUBIO_ERR("sink_sndfile: Aborted opening null path\n"); return NULL; } + if (s->path) AUBIO_FREE(s->path); + s->path = AUBIO_ARRAY(char_t, strnlen(path, PATH_MAX) + 1); + strncpy(s->path, path, strnlen(path, PATH_MAX) + 1); + s->samplerate = 0; s->channels = 0; - // negative samplerate given, abort - if ((sint_t)samplerate < 0) goto beach; // zero samplerate given. do not open yet - if ((sint_t)samplerate == 0) return s; + if ((sint_t)samplerate == 0) { + return s; + } + // invalid samplerate given, abort + if (aubio_io_validate_samplerate("sink_sndfile", s->path, samplerate)) { + goto beach; + } s->samplerate = samplerate; s->channels = 1; @@ -86,7 +92,9 @@ beach: uint_t aubio_sink_sndfile_preset_samplerate(aubio_sink_sndfile_t *s, uint_t samplerate) { - if ((sint_t)(samplerate) <= 0) return AUBIO_FAIL; + if (aubio_io_validate_samplerate("sink_sndfile", s->path, samplerate)) { + return AUBIO_FAIL; + } s->samplerate = samplerate; // automatically open when both samplerate and channels have been set if (s->samplerate != 0 && s->channels != 0) { @@ -97,7 +105,9 @@ uint_t aubio_sink_sndfile_preset_samplerate(aubio_sink_sndfile_t *s, uint_t samp uint_t aubio_sink_sndfile_preset_channels(aubio_sink_sndfile_t *s, uint_t channels) { - if ((sint_t)(channels) <= 0) return AUBIO_FAIL; + if (aubio_io_validate_channels("sink_sndfile", s->path, channels)) { + return AUBIO_FAIL; + } s->channels = channels; // automatically open when both samplerate and channels have been set if (s->samplerate != 0 && s->channels != 0) { @@ -106,12 +116,12 @@ uint_t aubio_sink_sndfile_preset_channels(aubio_sink_sndfile_t *s, uint_t channe return AUBIO_OK; } -uint_t aubio_sink_sndfile_get_samplerate(aubio_sink_sndfile_t *s) +uint_t aubio_sink_sndfile_get_samplerate(const aubio_sink_sndfile_t *s) { return s->samplerate; } -uint_t aubio_sink_sndfile_get_channels(aubio_sink_sndfile_t *s) +uint_t aubio_sink_sndfile_get_channels(const aubio_sink_sndfile_t *s) { return s->channels; } @@ -129,15 +139,17 @@ uint_t aubio_sink_sndfile_open(aubio_sink_sndfile_t *s) { if (s->handle == NULL) { /* show libsndfile err msg */ - AUBIO_ERR("sink_sndfile: Failed opening %s. %s\n", s->path, sf_strerror (NULL)); + AUBIO_ERR("sink_sndfile: Failed opening \"%s\" with %d channels, %dHz: %s\n", + s->path, s->channels, s->samplerate, sf_strerror (NULL)); return AUBIO_FAIL; } s->scratch_size = s->max_size*s->channels; /* allocate data for de/interleaving reallocated when needed. */ - if (s->scratch_size >= MAX_SIZE * MAX_CHANNELS) { + if (s->scratch_size >= MAX_SIZE * AUBIO_MAX_CHANNELS) { + abort(); AUBIO_ERR("sink_sndfile: %d x %d exceeds maximum aubio_sink_sndfile buffer size %d\n", - s->max_size, s->channels, MAX_CHANNELS * MAX_CHANNELS); + s->max_size, s->channels, MAX_SIZE * AUBIO_MAX_CHANNELS); return AUBIO_FAIL; } s->scratch_data = AUBIO_ARRAY(smpl_t,s->scratch_size); @@ -219,6 +231,7 @@ uint_t aubio_sink_sndfile_close (aubio_sink_sndfile_t *s) { void del_aubio_sink_sndfile(aubio_sink_sndfile_t * s){ if (!s) return; + if (s->path) AUBIO_FREE(s->path); aubio_sink_sndfile_close(s); AUBIO_FREE(s->scratch_data); AUBIO_FREE(s); diff --git a/src/io/sink_sndfile.h b/src/io/sink_sndfile.h index e5e07b2e..083c4ca2 100644 --- a/src/io/sink_sndfile.h +++ b/src/io/sink_sndfile.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SINK_SNDFILE_H -#define _AUBIO_SINK_SNDFILE_H +#ifndef AUBIO_SINK_SNDFILE_H +#define AUBIO_SINK_SNDFILE_H /** \file @@ -57,7 +57,7 @@ typedef struct _aubio_sink_sndfile_t aubio_sink_sndfile_t; been called. */ -aubio_sink_sndfile_t * new_aubio_sink_sndfile(char_t * uri, uint_t samplerate); +aubio_sink_sndfile_t * new_aubio_sink_sndfile(const char_t * uri, uint_t samplerate); /** @@ -101,7 +101,7 @@ uint_t aubio_sink_sndfile_preset_channels(aubio_sink_sndfile_t *s, uint_t channe \return samplerate, in Hz */ -uint_t aubio_sink_sndfile_get_samplerate(aubio_sink_sndfile_t *s); +uint_t aubio_sink_sndfile_get_samplerate(const aubio_sink_sndfile_t *s); /** @@ -111,7 +111,7 @@ uint_t aubio_sink_sndfile_get_samplerate(aubio_sink_sndfile_t *s); \return number of channels */ -uint_t aubio_sink_sndfile_get_channels(aubio_sink_sndfile_t *s); +uint_t aubio_sink_sndfile_get_channels(const aubio_sink_sndfile_t *s); /** @@ -159,4 +159,4 @@ void del_aubio_sink_sndfile(aubio_sink_sndfile_t * s); } #endif -#endif /* _AUBIO_SINK_SNDFILE_H */ +#endif /* AUBIO_SINK_SNDFILE_H */ diff --git a/src/io/sink_wavwrite.c b/src/io/sink_wavwrite.c index 7d832d0f..2e456ea7 100644 --- a/src/io/sink_wavwrite.c +++ b/src/io/sink_wavwrite.c @@ -19,18 +19,17 @@ */ -#include "config.h" +#include "aubio_priv.h" #ifdef HAVE_WAVWRITE -#include "aubio_priv.h" #include "fvec.h" #include "fmat.h" #include "io/sink_wavwrite.h" +#include "io/ioutils.h" #include -#define MAX_CHANNELS 6 #define MAX_SIZE 4096 #define FLOAT_TO_SHORT(x) (short)(x * 32768) @@ -68,8 +67,12 @@ struct _aubio_sink_wavwrite_t { unsigned short *scratch_data; }; -unsigned char *write_little_endian (unsigned int s, unsigned char *str, unsigned int length); -unsigned char *write_little_endian (unsigned int s, unsigned char *str, unsigned int length) { +static unsigned char *write_little_endian (unsigned int s, unsigned char *str, + unsigned int length); + +static unsigned char *write_little_endian (unsigned int s, unsigned char *str, + unsigned int length) +{ uint_t i; for (i = 0; i < length; i++) { str[i] = s >> (i * 8); @@ -77,7 +80,7 @@ unsigned char *write_little_endian (unsigned int s, unsigned char *str, unsigned return str; } -aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(char_t * path, uint_t samplerate) { +aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(const char_t * path, uint_t samplerate) { aubio_sink_wavwrite_t * s = AUBIO_NEW(aubio_sink_wavwrite_t); if (path == NULL) { @@ -89,7 +92,10 @@ aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(char_t * path, uint_t samplerate goto beach; } - s->path = path; + if (s->path) AUBIO_FREE(s->path); + s->path = AUBIO_ARRAY(char_t, strnlen(path, PATH_MAX) + 1); + strncpy(s->path, path, strnlen(path, PATH_MAX) + 1); + s->max_size = MAX_SIZE; s->bitspersample = 16; s->total_frames_written = 0; @@ -97,12 +103,14 @@ aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(char_t * path, uint_t samplerate s->samplerate = 0; s->channels = 0; - // negative samplerate given, abort - if ((sint_t)samplerate < 0) goto beach; // zero samplerate given. do not open yet - if ((sint_t)samplerate == 0) return s; - // samplerate way too large, fail - if ((sint_t)samplerate > 192000 * 4) goto beach; + if ((sint_t)samplerate == 0) { + return s; + } + // invalid samplerate given, abort + if (aubio_io_validate_samplerate("sink_wavwrite", s->path, samplerate)) { + goto beach; + } s->samplerate = samplerate; s->channels = 1; @@ -122,7 +130,9 @@ beach: uint_t aubio_sink_wavwrite_preset_samplerate(aubio_sink_wavwrite_t *s, uint_t samplerate) { - if ((sint_t)(samplerate) <= 0) return AUBIO_FAIL; + if (aubio_io_validate_samplerate("sink_wavwrite", s->path, samplerate)) { + return AUBIO_FAIL; + } s->samplerate = samplerate; // automatically open when both samplerate and channels have been set if (s->samplerate != 0 && s->channels != 0) { @@ -133,7 +143,9 @@ uint_t aubio_sink_wavwrite_preset_samplerate(aubio_sink_wavwrite_t *s, uint_t sa uint_t aubio_sink_wavwrite_preset_channels(aubio_sink_wavwrite_t *s, uint_t channels) { - if ((sint_t)(channels) <= 0) return AUBIO_FAIL; + if (aubio_io_validate_channels("sink_wavwrite", s->path, channels)) { + return AUBIO_FAIL; + } s->channels = channels; // automatically open when both samplerate and channels have been set if (s->samplerate != 0 && s->channels != 0) { @@ -142,12 +154,12 @@ uint_t aubio_sink_wavwrite_preset_channels(aubio_sink_wavwrite_t *s, uint_t chan return AUBIO_OK; } -uint_t aubio_sink_wavwrite_get_samplerate(aubio_sink_wavwrite_t *s) +uint_t aubio_sink_wavwrite_get_samplerate(const aubio_sink_wavwrite_t *s) { return s->samplerate; } -uint_t aubio_sink_wavwrite_get_channels(aubio_sink_wavwrite_t *s) +uint_t aubio_sink_wavwrite_get_channels(const aubio_sink_wavwrite_t *s) { return s->channels; } @@ -206,9 +218,9 @@ uint_t aubio_sink_wavwrite_open(aubio_sink_wavwrite_t *s) { s->scratch_size = s->max_size * s->channels; /* allocate data for de/interleaving reallocated when needed. */ - if (s->scratch_size >= MAX_SIZE * MAX_CHANNELS) { + if (s->scratch_size >= MAX_SIZE * AUBIO_MAX_CHANNELS) { AUBIO_ERR("sink_wavwrite: %d x %d exceeds SIZE maximum buffer size %d\n", - s->max_size, s->channels, MAX_SIZE * MAX_CHANNELS); + s->max_size, s->channels, MAX_SIZE * AUBIO_MAX_CHANNELS); goto beach; } s->scratch_data = AUBIO_ARRAY(unsigned short,s->scratch_size); @@ -287,6 +299,7 @@ uint_t aubio_sink_wavwrite_close(aubio_sink_wavwrite_t * s) { void del_aubio_sink_wavwrite(aubio_sink_wavwrite_t * s){ if (!s) return; aubio_sink_wavwrite_close(s); + if (s->path) AUBIO_FREE(s->path); AUBIO_FREE(s->scratch_data); AUBIO_FREE(s); } diff --git a/src/io/sink_wavwrite.h b/src/io/sink_wavwrite.h index 86b5819b..bb479e43 100644 --- a/src/io/sink_wavwrite.h +++ b/src/io/sink_wavwrite.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SINK_WAVWRITE_H -#define _AUBIO_SINK_WAVWRITE_H +#ifndef AUBIO_SINK_WAVWRITE_H +#define AUBIO_SINK_WAVWRITE_H /** \file @@ -57,7 +57,7 @@ typedef struct _aubio_sink_wavwrite_t aubio_sink_wavwrite_t; been called. */ -aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(char_t * uri, uint_t samplerate); +aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(const char_t * uri, uint_t samplerate); /** @@ -101,7 +101,7 @@ uint_t aubio_sink_wavwrite_preset_channels(aubio_sink_wavwrite_t *s, uint_t chan \return samplerate, in Hz */ -uint_t aubio_sink_wavwrite_get_samplerate(aubio_sink_wavwrite_t *s); +uint_t aubio_sink_wavwrite_get_samplerate(const aubio_sink_wavwrite_t *s); /** @@ -111,7 +111,7 @@ uint_t aubio_sink_wavwrite_get_samplerate(aubio_sink_wavwrite_t *s); \return number of channels */ -uint_t aubio_sink_wavwrite_get_channels(aubio_sink_wavwrite_t *s); +uint_t aubio_sink_wavwrite_get_channels(const aubio_sink_wavwrite_t *s); /** @@ -159,4 +159,4 @@ void del_aubio_sink_wavwrite(aubio_sink_wavwrite_t * s); } #endif -#endif /* _AUBIO_SINK_WAVWRITE_H */ +#endif /* AUBIO_SINK_WAVWRITE_H */ diff --git a/src/io/source.c b/src/io/source.c index 88e01800..41581bf5 100644 --- a/src/io/source.c +++ b/src/io/source.c @@ -18,7 +18,6 @@ */ -#include "config.h" #include "aubio_priv.h" #include "fvec.h" #include "fmat.h" @@ -40,6 +39,7 @@ typedef void (*aubio_source_do_t)(aubio_source_t * s, fvec_t * data, uint_t * re typedef void (*aubio_source_do_multi_t)(aubio_source_t * s, fmat_t * data, uint_t * read); typedef uint_t (*aubio_source_get_samplerate_t)(aubio_source_t * s); typedef uint_t (*aubio_source_get_channels_t)(aubio_source_t * s); +typedef uint_t (*aubio_source_get_duration_t)(aubio_source_t * s); typedef uint_t (*aubio_source_seek_t)(aubio_source_t * s, uint_t seek); typedef uint_t (*aubio_source_close_t)(aubio_source_t * s); typedef void (*del_aubio_source_t)(aubio_source_t * s); @@ -50,20 +50,22 @@ struct _aubio_source_t { aubio_source_do_multi_t s_do_multi; aubio_source_get_samplerate_t s_get_samplerate; aubio_source_get_channels_t s_get_channels; + aubio_source_get_duration_t s_get_duration; aubio_source_seek_t s_seek; aubio_source_close_t s_close; del_aubio_source_t s_del; }; -aubio_source_t * new_aubio_source(char_t * uri, uint_t samplerate, uint_t hop_size) { +aubio_source_t * new_aubio_source(const char_t * uri, uint_t samplerate, uint_t hop_size) { aubio_source_t * s = AUBIO_NEW(aubio_source_t); -#if HAVE_LIBAV +#ifdef HAVE_LIBAV s->source = (void *)new_aubio_source_avcodec(uri, samplerate, hop_size); if (s->source) { s->s_do = (aubio_source_do_t)(aubio_source_avcodec_do); s->s_do_multi = (aubio_source_do_multi_t)(aubio_source_avcodec_do_multi); s->s_get_channels = (aubio_source_get_channels_t)(aubio_source_avcodec_get_channels); s->s_get_samplerate = (aubio_source_get_samplerate_t)(aubio_source_avcodec_get_samplerate); + s->s_get_duration = (aubio_source_get_duration_t)(aubio_source_avcodec_get_duration); s->s_seek = (aubio_source_seek_t)(aubio_source_avcodec_seek); s->s_close = (aubio_source_close_t)(aubio_source_avcodec_close); s->s_del = (del_aubio_source_t)(del_aubio_source_avcodec); @@ -77,40 +79,48 @@ aubio_source_t * new_aubio_source(char_t * uri, uint_t samplerate, uint_t hop_si s->s_do_multi = (aubio_source_do_multi_t)(aubio_source_apple_audio_do_multi); s->s_get_channels = (aubio_source_get_channels_t)(aubio_source_apple_audio_get_channels); s->s_get_samplerate = (aubio_source_get_samplerate_t)(aubio_source_apple_audio_get_samplerate); + s->s_get_duration = (aubio_source_get_duration_t)(aubio_source_apple_audio_get_duration); s->s_seek = (aubio_source_seek_t)(aubio_source_apple_audio_seek); s->s_close = (aubio_source_close_t)(aubio_source_apple_audio_close); s->s_del = (del_aubio_source_t)(del_aubio_source_apple_audio); return s; } #endif /* HAVE_SOURCE_APPLE_AUDIO */ -#if HAVE_SNDFILE +#ifdef HAVE_SNDFILE s->source = (void *)new_aubio_source_sndfile(uri, samplerate, hop_size); if (s->source) { s->s_do = (aubio_source_do_t)(aubio_source_sndfile_do); s->s_do_multi = (aubio_source_do_multi_t)(aubio_source_sndfile_do_multi); s->s_get_channels = (aubio_source_get_channels_t)(aubio_source_sndfile_get_channels); s->s_get_samplerate = (aubio_source_get_samplerate_t)(aubio_source_sndfile_get_samplerate); + s->s_get_duration = (aubio_source_get_duration_t)(aubio_source_sndfile_get_duration); s->s_seek = (aubio_source_seek_t)(aubio_source_sndfile_seek); s->s_close = (aubio_source_close_t)(aubio_source_sndfile_close); s->s_del = (del_aubio_source_t)(del_aubio_source_sndfile); return s; } #endif /* HAVE_SNDFILE */ -#if HAVE_WAVREAD +#ifdef HAVE_WAVREAD s->source = (void *)new_aubio_source_wavread(uri, samplerate, hop_size); if (s->source) { s->s_do = (aubio_source_do_t)(aubio_source_wavread_do); s->s_do_multi = (aubio_source_do_multi_t)(aubio_source_wavread_do_multi); s->s_get_channels = (aubio_source_get_channels_t)(aubio_source_wavread_get_channels); s->s_get_samplerate = (aubio_source_get_samplerate_t)(aubio_source_wavread_get_samplerate); + s->s_get_duration = (aubio_source_get_duration_t)(aubio_source_wavread_get_duration); s->s_seek = (aubio_source_seek_t)(aubio_source_wavread_seek); s->s_close = (aubio_source_close_t)(aubio_source_wavread_close); s->s_del = (del_aubio_source_t)(del_aubio_source_wavread); return s; } #endif /* HAVE_WAVREAD */ - AUBIO_ERROR("source: failed creating aubio source with %s" - " at samplerate %d with hop_size %d\n", uri, samplerate, hop_size); +#if !defined(HAVE_WAVREAD) && \ + !defined(HAVE_LIBAV) && \ + !defined(HAVE_SOURCE_APPLE_AUDIO) && \ + !defined(HAVE_SNDFILE) + AUBIO_ERROR("source: failed creating with %s at %dHz with hop size %d" + " (no source built-in)\n", uri, samplerate, hop_size); +#endif AUBIO_FREE(s); return NULL; } @@ -141,6 +151,10 @@ uint_t aubio_source_get_channels(aubio_source_t * s) { return s->s_get_channels((void *)s->source); } +uint_t aubio_source_get_duration(aubio_source_t *s) { + return s->s_get_duration((void *)s->source); +} + uint_t aubio_source_seek (aubio_source_t * s, uint_t seek ) { return s->s_seek((void *)s->source, seek); } diff --git a/src/io/source.h b/src/io/source.h index e31cc6af..2df25843 100644 --- a/src/io/source.h +++ b/src/io/source.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SOURCE_H -#define _AUBIO_SOURCE_H +#ifndef AUBIO_SOURCE_H +#define AUBIO_SOURCE_H /** \file @@ -85,7 +85,7 @@ typedef struct _aubio_source_t aubio_source_t; ::aubio_source_get_samplerate. */ -aubio_source_t * new_aubio_source(char_t * uri, uint_t samplerate, uint_t hop_size); +aubio_source_t * new_aubio_source(const char_t * uri, uint_t samplerate, uint_t hop_size); /** @@ -149,6 +149,16 @@ uint_t aubio_source_seek (aubio_source_t * s, uint_t pos); /** + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source + \return number of frames in file + +*/ +uint_t aubio_source_get_duration (aubio_source_t * s); + +/** + close source object \param s source object, created with ::new_aubio_source @@ -171,4 +181,4 @@ void del_aubio_source(aubio_source_t * s); } #endif -#endif /* _AUBIO_SOURCE_H */ +#endif /* AUBIO_SOURCE_H */ diff --git a/src/io/source_apple_audio.c b/src/io/source_apple_audio.c index 0cf7d44b..db7ff54c 100644 --- a/src/io/source_apple_audio.c +++ b/src/io/source_apple_audio.c @@ -18,11 +18,10 @@ */ -#include "config.h" +#include "aubio_priv.h" #ifdef HAVE_SOURCE_APPLE_AUDIO -#include "aubio_priv.h" #include "fvec.h" #include "fmat.h" #include "io/source_apple_audio.h" @@ -54,9 +53,9 @@ extern void freeAudioBufferList(AudioBufferList *bufferList); extern CFURLRef createURLFromPath(const char * path); char_t *getPrintableOSStatusError(char_t *str, OSStatus error); -uint_t aubio_source_apple_audio_open (aubio_source_apple_audio_t *s, char_t * path); +uint_t aubio_source_apple_audio_open (aubio_source_apple_audio_t *s, const char_t * path); -aubio_source_apple_audio_t * new_aubio_source_apple_audio(char_t * path, uint_t samplerate, uint_t block_size) +aubio_source_apple_audio_t * new_aubio_source_apple_audio(const char_t * path, uint_t samplerate, uint_t block_size) { aubio_source_apple_audio_t * s = AUBIO_NEW(aubio_source_apple_audio_t); @@ -79,7 +78,6 @@ aubio_source_apple_audio_t * new_aubio_source_apple_audio(char_t * path, uint_t s->block_size = block_size; s->samplerate = samplerate; - s->path = path; if ( aubio_source_apple_audio_open ( s, path ) ) { goto beach; @@ -91,14 +89,17 @@ beach: return NULL; } -uint_t aubio_source_apple_audio_open (aubio_source_apple_audio_t *s, char_t * path) +uint_t aubio_source_apple_audio_open (aubio_source_apple_audio_t *s, const char_t * path) { OSStatus err = noErr; UInt32 propSize; - s->path = path; + + if (s->path) AUBIO_FREE(s->path); + s->path = AUBIO_ARRAY(char_t, strnlen(path, PATH_MAX) + 1); + strncpy(s->path, path, strnlen(path, PATH_MAX) + 1); // open the resource url - CFURLRef fileURL = createURLFromPath(path); + CFURLRef fileURL = createURLFromPath(s->path); err = ExtAudioFileOpenURL(fileURL, &s->audioFile); CFRelease(fileURL); if (err == -43) { @@ -278,7 +279,7 @@ beach: uint_t aubio_source_apple_audio_close (aubio_source_apple_audio_t *s) { OSStatus err = noErr; - if (!s->audioFile) { return AUBIO_FAIL; } + if (!s->audioFile) { return AUBIO_OK; } err = ExtAudioFileDispose(s->audioFile); s->audioFile = NULL; if (err) { @@ -293,6 +294,7 @@ uint_t aubio_source_apple_audio_close (aubio_source_apple_audio_t *s) void del_aubio_source_apple_audio(aubio_source_apple_audio_t * s){ aubio_source_apple_audio_close (s); + if (s->path) AUBIO_FREE(s->path); freeAudioBufferList(&s->bufferList); AUBIO_FREE(s); return; @@ -308,10 +310,7 @@ uint_t aubio_source_apple_audio_seek (aubio_source_apple_audio_t * s, uint_t pos goto beach; } // check if we are not seeking out of the file - SInt64 fileLengthFrames = 0; - UInt32 propSize = sizeof(fileLengthFrames); - ExtAudioFileGetProperty(s->audioFile, - kExtAudioFileProperty_FileLengthFrames, &propSize, &fileLengthFrames); + uint_t fileLengthFrames = aubio_source_apple_audio_get_duration(s); // compute position in the source file, before resampling smpl_t ratio = s->source_samplerate * 1. / s->samplerate; SInt64 resampled_pos = (SInt64)ROUND( pos * ratio ); @@ -352,12 +351,27 @@ beach: return err; } -uint_t aubio_source_apple_audio_get_samplerate(aubio_source_apple_audio_t * s) { +uint_t aubio_source_apple_audio_get_samplerate(const aubio_source_apple_audio_t * s) { return s->samplerate; } -uint_t aubio_source_apple_audio_get_channels(aubio_source_apple_audio_t * s) { +uint_t aubio_source_apple_audio_get_channels(const aubio_source_apple_audio_t * s) { return s->channels; } +uint_t aubio_source_apple_audio_get_duration(const aubio_source_apple_audio_t * s) { + SInt64 fileLengthFrames = 0; + UInt32 propSize = sizeof(fileLengthFrames); + OSStatus err = ExtAudioFileGetProperty(s->audioFile, + kExtAudioFileProperty_FileLengthFrames, &propSize, &fileLengthFrames); + if (err) { + char_t errorstr[20]; + AUBIO_ERROR("source_apple_audio: Failed getting %s duration, " + "error in ExtAudioFileGetProperty (%s)\n", s->path, + getPrintableOSStatusError(errorstr, err)); + return err; + } + return (uint_t)fileLengthFrames; +} + #endif /* HAVE_SOURCE_APPLE_AUDIO */ diff --git a/src/io/source_apple_audio.h b/src/io/source_apple_audio.h index 61dbeba9..0ad55694 100644 --- a/src/io/source_apple_audio.h +++ b/src/io/source_apple_audio.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SOURCE_APPLE_AUDIO_H -#define _AUBIO_SOURCE_APPLE_AUDIO_H +#ifndef AUBIO_SOURCE_APPLE_AUDIO_H +#define AUBIO_SOURCE_APPLE_AUDIO_H /** \file @@ -57,7 +57,7 @@ typedef struct _aubio_source_apple_audio_t aubio_source_apple_audio_t; ::aubio_source_apple_audio_get_samplerate. */ -aubio_source_apple_audio_t * new_aubio_source_apple_audio(char_t * uri, uint_t samplerate, uint_t hop_size); +aubio_source_apple_audio_t * new_aubio_source_apple_audio(const char_t * uri, uint_t samplerate, uint_t hop_size); /** @@ -95,7 +95,7 @@ void aubio_source_apple_audio_do_multi(aubio_source_apple_audio_t * s, fmat_t * \return samplerate, in Hz */ -uint_t aubio_source_apple_audio_get_samplerate(aubio_source_apple_audio_t * s); +uint_t aubio_source_apple_audio_get_samplerate(const aubio_source_apple_audio_t * s); /** @@ -105,7 +105,17 @@ uint_t aubio_source_apple_audio_get_samplerate(aubio_source_apple_audio_t * s); \return number of channels */ -uint_t aubio_source_apple_audio_get_channels(aubio_source_apple_audio_t * s); +uint_t aubio_source_apple_audio_get_channels(const aubio_source_apple_audio_t * s); + +/** + + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_apple_audio + \return number of frames in file + +*/ +uint_t aubio_source_apple_audio_get_duration(const aubio_source_apple_audio_t * s); /** @@ -143,4 +153,4 @@ void del_aubio_source_apple_audio(aubio_source_apple_audio_t * s); } #endif -#endif /* _AUBIO_SOURCE_APPLE_AUDIO_H */ +#endif /* AUBIO_SOURCE_APPLE_AUDIO_H */ diff --git a/src/io/source_avcodec.c b/src/io/source_avcodec.c index 5cfd27de..4ed64993 100644 --- a/src/io/source_avcodec.c +++ b/src/io/source_avcodec.c @@ -18,20 +18,34 @@ */ - -#include "config.h" +#include "aubio_priv.h" #ifdef HAVE_LIBAV -// determine whether we use libavformat from ffmpe or libav -#define FFMPEG_LIBAVFORMAT (LIBAVFORMAT_VERSION_MICRO > 99) - #include #include #include #include #include +// determine whether we use libavformat from ffmpeg or from libav +#define FFMPEG_LIBAVFORMAT (LIBAVFORMAT_VERSION_MICRO > 99 ) +// max_analyze_duration2 was used from ffmpeg libavformat 55.43.100 through 57.2.100 +#define FFMPEG_LIBAVFORMAT_MAX_DUR2 FFMPEG_LIBAVFORMAT && ( \ + (LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR >= 43) \ + || (LIBAVFORMAT_VERSION_MAJOR == 56) \ + || (LIBAVFORMAT_VERSION_MAJOR == 57 && LIBAVFORMAT_VERSION_MINOR < 2) \ + ) + +// backward compatibility with libavcodec55 +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) +#warning "libavcodec55 is deprecated" +#define HAVE_AUBIO_LIBAVCODEC_DEPRECATED 1 +#define av_frame_alloc avcodec_alloc_frame +#define av_frame_free avcodec_free_frame +#define av_packet_unref av_free_packet +#endif + #include "aubio_priv.h" #include "fvec.h" #include "fmat.h" @@ -53,8 +67,9 @@ struct _aubio_source_avcodec_t { AVFormatContext *avFormatCtx; AVCodecContext *avCodecCtx; AVFrame *avFrame; + AVPacket avPacket; AVAudioResampleContext *avr; - float *output; + smpl_t *output; uint_t read_samples; uint_t read_index; sint_t selected_stream; @@ -66,7 +81,22 @@ struct _aubio_source_avcodec_t { void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s, uint_t multi); void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s, uint_t * read_samples); -aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplerate, uint_t hop_size) { +uint_t aubio_source_avcodec_has_network_url(aubio_source_avcodec_t *s); + +uint_t aubio_source_avcodec_has_network_url(aubio_source_avcodec_t *s) { + char proto[20], authorization[256], hostname[128], uripath[256]; + int proto_size = 20, authorization_size = 256, hostname_size = 128, + *port_ptr = 0, path_size = 256; + av_url_split(proto, proto_size, authorization, authorization_size, hostname, + hostname_size, port_ptr, uripath, path_size, s->path); + if (strlen(proto)) { + return 1; + } + return 0; +} + + +aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * path, uint_t samplerate, uint_t hop_size) { aubio_source_avcodec_t * s = AUBIO_NEW(aubio_source_avcodec_t); int err; if (path == NULL) { @@ -84,13 +114,17 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplera s->hop_size = hop_size; s->channels = 1; - s->path = path; + + if (s->path) AUBIO_FREE(s->path); + s->path = AUBIO_ARRAY(char_t, strnlen(path, PATH_MAX) + 1); + strncpy(s->path, path, strnlen(path, PATH_MAX) + 1); // register all formats and codecs av_register_all(); - // if path[0] != '/' - //avformat_network_init(); + if (aubio_source_avcodec_has_network_url(s)) { + avformat_network_init(); + } // try opening the file and get some info about it AVFormatContext *avFormatCtx = s->avFormatCtx; @@ -103,7 +137,7 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplera } // try to make sure max_analyze_duration is big enough for most songs -#if FFMPEG_LIBAVFORMAT +#if FFMPEG_LIBAVFORMAT_MAX_DUR2 avFormatCtx->max_analyze_duration2 *= 100; #else avFormatCtx->max_analyze_duration *= 100; @@ -125,7 +159,11 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplera uint_t i; sint_t selected_stream = -1; for (i = 0; i < avFormatCtx->nb_streams; i++) { +#if FF_API_LAVF_AVCTX + if (avFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { +#else if (avFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { +#endif if (selected_stream == -1) { selected_stream = i; } else { @@ -142,13 +180,39 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplera s->selected_stream = selected_stream; AVCodecContext *avCodecCtx = s->avCodecCtx; +#if FF_API_LAVF_AVCTX + AVCodecParameters *codecpar = avFormatCtx->streams[selected_stream]->codecpar; + if (codecpar == NULL) { + AUBIO_ERR("source_avcodec: Could not find decoder for %s", s->path); + goto beach; + } + AVCodec *codec = avcodec_find_decoder(codecpar->codec_id); + + /* Allocate a codec context for the decoder */ + avCodecCtx = avcodec_alloc_context3(codec); + if (!avCodecCtx) { + AUBIO_ERR("source_avcodec: Failed to allocate the %s codec context for path %s\n", + av_get_media_type_string(AVMEDIA_TYPE_AUDIO), s->path); + goto beach; + } +#else avCodecCtx = avFormatCtx->streams[selected_stream]->codec; AVCodec *codec = avcodec_find_decoder(avCodecCtx->codec_id); +#endif if (codec == NULL) { AUBIO_ERR("source_avcodec: Could not find decoder for %s", s->path); goto beach; } +#if FF_API_LAVF_AVCTX + /* Copy codec parameters from input stream to output codec context */ + if ((err = avcodec_parameters_to_context(avCodecCtx, codecpar)) < 0) { + AUBIO_ERR("source_avcodec: Failed to copy %s codec parameters to decoder context for %s\n", + av_get_media_type_string(AVMEDIA_TYPE_AUDIO), s->path); + goto beach; + } +#endif + if ( ( err = avcodec_open2(avCodecCtx, codec, NULL) ) < 0) { char errorstr[256]; av_strerror (err, errorstr, sizeof(errorstr)); @@ -163,10 +227,10 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplera //AUBIO_DBG("input_channels: %d\n", s->input_channels); if (samplerate == 0) { - samplerate = s->input_samplerate; - //AUBIO_DBG("sampling rate set to 0, automagically adjusting to %d\n", samplerate); + s->samplerate = s->input_samplerate; + } else { + s->samplerate = samplerate; } - s->samplerate = samplerate; if (s->samplerate > s->input_samplerate) { AUBIO_WRN("source_avcodec: upsampling %s from %d to %d\n", s->path, @@ -180,7 +244,7 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplera } /* allocate output for avr */ - s->output = (float *)av_malloc(AUBIO_AVCODEC_MAX_BUFFER_SIZE * sizeof(float)); + s->output = (smpl_t *)av_malloc(AUBIO_AVCODEC_MAX_BUFFER_SIZE * sizeof(smpl_t)); s->read_samples = 0; s->read_index = 0; @@ -207,24 +271,26 @@ beach: } void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s, uint_t multi) { + // create or reset resampler to/from mono/multi-channel if ( (multi != s->multi) || (s->avr == NULL) ) { int64_t input_layout = av_get_default_channel_layout(s->input_channels); uint_t output_channels = multi ? s->input_channels : 1; int64_t output_layout = av_get_default_channel_layout(output_channels); - if (s->avr != NULL) { - avresample_close( s->avr ); - av_free ( s->avr ); - s->avr = NULL; - } - AVAudioResampleContext *avr = s->avr; - avr = avresample_alloc_context(); + AVAudioResampleContext *avr = avresample_alloc_context(); + AVAudioResampleContext *oldavr = s->avr; av_opt_set_int(avr, "in_channel_layout", input_layout, 0); av_opt_set_int(avr, "out_channel_layout", output_layout, 0); av_opt_set_int(avr, "in_sample_rate", s->input_samplerate, 0); av_opt_set_int(avr, "out_sample_rate", s->samplerate, 0); av_opt_set_int(avr, "in_sample_fmt", s->avCodecCtx->sample_fmt, 0); +#if HAVE_AUBIO_DOUBLE + av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_DBL, 0); +#else av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); +#endif + // TODO: use planar? + //av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); int err; if ( ( err = avresample_open(avr) ) < 0) { char errorstr[256]; @@ -235,6 +301,11 @@ void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s, uint_t mul return; } s->avr = avr; + if (oldavr != NULL) { + avresample_close( oldavr ); + av_free ( oldavr ); + oldavr = NULL; + } s->multi = multi; } } @@ -243,10 +314,10 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s, uint_t * read_sam AVFormatContext *avFormatCtx = s->avFormatCtx; AVCodecContext *avCodecCtx = s->avCodecCtx; AVFrame *avFrame = s->avFrame; - AVPacket avPacket; + AVPacket avPacket = s->avPacket; av_init_packet (&avPacket); AVAudioResampleContext *avr = s->avr; - float *output = s->output; + smpl_t *output = s->output; *read_samples = 0; do @@ -259,20 +330,42 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s, uint_t * read_sam if (err != 0) { char errorstr[256]; av_strerror (err, errorstr, sizeof(errorstr)); - AUBIO_ERR("Could not read frame in %s (%s)\n", s->path, errorstr); + AUBIO_ERR("source_avcodec: could not read frame in %s (%s)\n", s->path, errorstr); goto beach; } } while (avPacket.stream_index != s->selected_stream); int got_frame = 0; +#if FF_API_LAVF_AVCTX + int ret = avcodec_send_packet(avCodecCtx, &avPacket); + if (ret < 0 && ret != AVERROR_EOF) { + AUBIO_ERR("source_avcodec: error when sending packet for %s\n", s->path); + goto beach; + } + ret = avcodec_receive_frame(avCodecCtx, avFrame); + if (ret >= 0) { + got_frame = 1; + } + if (ret < 0) { + if (ret == AVERROR(EAGAIN)) { + AUBIO_WRN("source_avcodec: output is not available right now - user must try to send new input\n"); + } else if (ret == AVERROR_EOF) { + AUBIO_WRN("source_avcodec: the decoder has been fully flushed, and there will be no more output frames\n"); + } else { + AUBIO_ERR("source_avcodec: decoding errors on %s\n", s->path); + goto beach; + } + } +#else int len = avcodec_decode_audio4(avCodecCtx, avFrame, &got_frame, &avPacket); if (len < 0) { - AUBIO_ERR("Error while decoding %s\n", s->path); + AUBIO_ERR("source_avcodec: error while decoding %s\n", s->path); goto beach; } +#endif if (got_frame == 0) { - //AUBIO_ERR("Could not get frame for (%s)\n", s->path); + AUBIO_WRN("source_avcodec: did not get a frame when reading %s\n", s->path); goto beach; } @@ -286,7 +379,7 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s, uint_t * read_sam (uint8_t **)&output, out_linesize, max_out_samples, (uint8_t **)avFrame->data, in_linesize, in_samples); if (out_samples <= 0) { - //AUBIO_ERR("No sample found while converting frame (%s)\n", s->path); + AUBIO_WRN("source_avcodec: no sample found while converting frame (%s)\n", s->path); goto beach; } @@ -299,14 +392,15 @@ beach: s->avr = avr; s->output = output; - av_free_packet(&avPacket); + av_packet_unref(&avPacket); } void aubio_source_avcodec_do(aubio_source_avcodec_t * s, fvec_t * read_data, uint_t * read){ - if (s->multi == 1) aubio_source_avcodec_reset_resampler(s, 0); uint_t i; uint_t end = 0; uint_t total_wrote = 0; + // switch from multi + if (s->multi == 1) aubio_source_avcodec_reset_resampler(s, 0); while (total_wrote < s->hop_size) { end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote); for (i = 0; i < end; i++) { @@ -326,7 +420,7 @@ void aubio_source_avcodec_do(aubio_source_avcodec_t * s, fvec_t * read_data, uin } } if (total_wrote < s->hop_size) { - for (i = end; i < s->hop_size; i++) { + for (i = total_wrote; i < s->hop_size; i++) { read_data->data[i] = 0.; } } @@ -334,10 +428,11 @@ void aubio_source_avcodec_do(aubio_source_avcodec_t * s, fvec_t * read_data, uin } void aubio_source_avcodec_do_multi(aubio_source_avcodec_t * s, fmat_t * read_data, uint_t * read){ - if (s->multi == 0) aubio_source_avcodec_reset_resampler(s, 1); uint_t i,j; uint_t end = 0; uint_t total_wrote = 0; + // switch from mono + if (s->multi == 0) aubio_source_avcodec_reset_resampler(s, 1); while (total_wrote < s->hop_size) { end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote); for (j = 0; j < read_data->height; j++) { @@ -361,7 +456,7 @@ void aubio_source_avcodec_do_multi(aubio_source_avcodec_t * s, fmat_t * read_dat } if (total_wrote < s->hop_size) { for (j = 0; j < read_data->height; j++) { - for (i = end; i < s->hop_size; i++) { + for (i = total_wrote; i < s->hop_size; i++) { read_data->data[j][i] = 0.; } } @@ -369,11 +464,11 @@ void aubio_source_avcodec_do_multi(aubio_source_avcodec_t * s, fmat_t * read_dat *read = total_wrote; } -uint_t aubio_source_avcodec_get_samplerate(aubio_source_avcodec_t * s) { +uint_t aubio_source_avcodec_get_samplerate(const aubio_source_avcodec_t * s) { return s->samplerate; } -uint_t aubio_source_avcodec_get_channels(aubio_source_avcodec_t * s) { +uint_t aubio_source_avcodec_get_channels(const aubio_source_avcodec_t * s) { return s->input_channels; } @@ -382,10 +477,22 @@ uint_t aubio_source_avcodec_seek (aubio_source_avcodec_t * s, uint_t pos) { int64_t min_ts = MAX(resampled_pos - 2000, 0); int64_t max_ts = MIN(resampled_pos + 2000, INT64_MAX); int seek_flags = AVSEEK_FLAG_FRAME | AVSEEK_FLAG_ANY; - int ret = avformat_seek_file(s->avFormatCtx, s->selected_stream, + int ret = AUBIO_FAIL; + if (s->avFormatCtx != NULL && s->avr != NULL) { + ret = AUBIO_OK; + } else { + AUBIO_ERR("source_avcodec: failed seeking in %s (file not opened?)", s->path); + return ret; + } + if ((sint_t)pos < 0) { + AUBIO_ERR("source_avcodec: could not seek %s at %d (seeking position" + " should be >= 0)\n", s->path, pos); + return AUBIO_FAIL; + } + ret = avformat_seek_file(s->avFormatCtx, s->selected_stream, min_ts, resampled_pos, max_ts, seek_flags); if (ret < 0) { - AUBIO_ERR("Failed seeking to %d in file %s", pos, s->path); + AUBIO_ERR("source_avcodec: failed seeking to %d in file %s", pos, s->path); } // reset read status s->eof = 0; @@ -397,6 +504,14 @@ uint_t aubio_source_avcodec_seek (aubio_source_avcodec_t * s, uint_t pos) { return ret; } +uint_t aubio_source_avcodec_get_duration (aubio_source_avcodec_t * s) { + if (s && &(s->avFormatCtx) != NULL) { + int64_t duration = s->avFormatCtx->duration; + return s->samplerate * ((uint_t)duration / 1e6 ); + } + return 0; +} + uint_t aubio_source_avcodec_close(aubio_source_avcodec_t * s) { if (s->avr != NULL) { avresample_close( s->avr ); @@ -408,9 +523,13 @@ uint_t aubio_source_avcodec_close(aubio_source_avcodec_t * s) { } s->avCodecCtx = NULL; if (s->avFormatCtx != NULL) { - avformat_close_input ( &(s->avFormatCtx) ); + avformat_close_input(&s->avFormatCtx); +#ifndef HAVE_AUBIO_LIBAVCODEC_DEPRECATED // avoid crash on old libavcodec54 + avformat_free_context(s->avFormatCtx); +#endif + s->avFormatCtx = NULL; } - s->avFormatCtx = NULL; + av_packet_unref(&s->avPacket); return AUBIO_OK; } @@ -425,6 +544,10 @@ void del_aubio_source_avcodec(aubio_source_avcodec_t * s){ av_frame_free( &(s->avFrame) ); } s->avFrame = NULL; + if (s->path) { + AUBIO_FREE(s->path); + } + s->path = NULL; AUBIO_FREE(s); } diff --git a/src/io/source_avcodec.h b/src/io/source_avcodec.h index a5f6845f..81f657d6 100644 --- a/src/io/source_avcodec.h +++ b/src/io/source_avcodec.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SOURCE_AVCODEC_H -#define _AUBIO_SOURCE_AVCODEC_H +#ifndef AUBIO_SOURCE_AVCODEC_H +#define AUBIO_SOURCE_AVCODEC_H /** \file @@ -56,7 +56,7 @@ typedef struct _aubio_source_avcodec_t aubio_source_avcodec_t; ::aubio_source_avcodec_get_samplerate. */ -aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * uri, uint_t samplerate, uint_t hop_size); +aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * uri, uint_t samplerate, uint_t hop_size); /** @@ -94,7 +94,7 @@ void aubio_source_avcodec_do_multi(aubio_source_avcodec_t * s, fmat_t * read_to, \return samplerate, in Hz */ -uint_t aubio_source_avcodec_get_samplerate(aubio_source_avcodec_t * s); +uint_t aubio_source_avcodec_get_samplerate(const aubio_source_avcodec_t * s); /** @@ -104,7 +104,7 @@ uint_t aubio_source_avcodec_get_samplerate(aubio_source_avcodec_t * s); \return number of channels */ -uint_t aubio_source_avcodec_get_channels (aubio_source_avcodec_t * s); +uint_t aubio_source_avcodec_get_channels (const aubio_source_avcodec_t * s); /** @@ -120,6 +120,16 @@ uint_t aubio_source_avcodec_seek (aubio_source_avcodec_t *s, uint_t pos); /** + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_avcodec + \return number of frames in file + +*/ +uint_t aubio_source_avcodec_get_duration (aubio_source_avcodec_t * s); + +/** + close source \param s source object, created with ::new_aubio_source_avcodec @@ -142,4 +152,4 @@ void del_aubio_source_avcodec(aubio_source_avcodec_t * s); } #endif -#endif /* _AUBIO_SOURCE_AVCODEC_H */ +#endif /* AUBIO_SOURCE_AVCODEC_H */ diff --git a/src/io/source_sndfile.c b/src/io/source_sndfile.c index a2ef1f24..eea2ffce 100644 --- a/src/io/source_sndfile.c +++ b/src/io/source_sndfile.c @@ -18,23 +18,26 @@ */ - -#include "config.h" +#include "aubio_priv.h" #ifdef HAVE_SNDFILE #include -#include "aubio_priv.h" #include "fvec.h" #include "fmat.h" #include "source_sndfile.h" #include "temporal/resampler.h" -#define MAX_CHANNELS 6 #define MAX_SIZE 4096 -#define MAX_SAMPLES MAX_CHANNELS * MAX_SIZE +#define MAX_SAMPLES AUBIO_MAX_CHANNELS * MAX_SIZE + +#if !HAVE_AUBIO_DOUBLE +#define aubio_sf_read_smpl sf_read_float +#else /* HAVE_AUBIO_DOUBLE */ +#define aubio_sf_read_smpl sf_read_double +#endif /* HAVE_AUBIO_DOUBLE */ struct _aubio_source_sndfile_t { uint_t hop_size; @@ -47,21 +50,23 @@ struct _aubio_source_sndfile_t { int input_samplerate; int input_channels; int input_format; + int duration; // resampling stuff smpl_t ratio; uint_t input_hop_size; #ifdef HAVE_SAMPLERATE - aubio_resampler_t *resampler; + aubio_resampler_t **resamplers; fvec_t *input_data; + fmat_t *input_mat; #endif /* HAVE_SAMPLERATE */ // some temporary memory for sndfile to write at uint_t scratch_size; - float *scratch_data; + smpl_t *scratch_data; }; -aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * path, uint_t samplerate, uint_t hop_size) { +aubio_source_sndfile_t * new_aubio_source_sndfile(const char_t * path, uint_t samplerate, uint_t hop_size) { aubio_source_sndfile_t * s = AUBIO_NEW(aubio_source_sndfile_t); SF_INFO sfinfo; @@ -80,7 +85,10 @@ aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * path, uint_t samplera s->hop_size = hop_size; s->channels = 1; - s->path = path; + + if (s->path) AUBIO_FREE(s->path); + s->path = AUBIO_ARRAY(char_t, strnlen(path, PATH_MAX) + 1); + strncpy(s->path, path, strnlen(path, PATH_MAX) + 1); // try opening the file, getting the info in sfinfo AUBIO_MEMSET(&sfinfo, 0, sizeof (sfinfo)); @@ -88,7 +96,8 @@ aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * path, uint_t samplera if (s->handle == NULL) { /* show libsndfile err msg */ - AUBIO_ERR("source_sndfile: Failed opening %s: %s\n", s->path, sf_strerror (NULL)); + AUBIO_ERR("source_sndfile: Failed opening %s (%s)\n", s->path, + sf_strerror (NULL)); goto beach; } @@ -96,14 +105,16 @@ aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * path, uint_t samplera s->input_samplerate = sfinfo.samplerate; s->input_channels = sfinfo.channels; s->input_format = sfinfo.format; + s->duration = sfinfo.frames; if (samplerate == 0) { - samplerate = s->input_samplerate; + s->samplerate = s->input_samplerate; //AUBIO_DBG("sampling rate set to 0, automagically adjusting to %d\n", samplerate); + } else { + s->samplerate = samplerate; } - s->samplerate = samplerate; /* compute input block size required before resampling */ - s->ratio = s->samplerate/(float)s->input_samplerate; + s->ratio = s->samplerate/(smpl_t)s->input_samplerate; s->input_hop_size = (uint_t)FLOOR(s->hop_size / s->ratio + .5); if (s->input_hop_size * s->input_channels > MAX_SAMPLES) { @@ -113,14 +124,20 @@ aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * path, uint_t samplera } #ifdef HAVE_SAMPLERATE - s->resampler = NULL; s->input_data = NULL; + s->input_mat = NULL; + s->resamplers = NULL; if (s->ratio != 1) { + uint_t i; + s->resamplers = AUBIO_ARRAY(aubio_resampler_t*, s->input_channels); s->input_data = new_fvec(s->input_hop_size); - s->resampler = new_aubio_resampler(s->ratio, 4); + s->input_mat = new_fmat(s->input_channels, s->input_hop_size); + for (i = 0; i < (uint_t)s->input_channels; i++) { + s->resamplers[i] = new_aubio_resampler(s->ratio, 4); + } if (s->ratio > 1) { // we would need to add a ring buffer for these - if ( (uint_t)(s->input_hop_size * s->ratio + .5) != s->hop_size ) { + if ( (uint_t)FLOOR(s->input_hop_size * s->ratio + .5) != s->hop_size ) { AUBIO_ERR("source_sndfile: can not upsample %s from %d to %d\n", s->path, s->input_samplerate, s->samplerate); goto beach; @@ -128,6 +145,7 @@ aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * path, uint_t samplera AUBIO_WRN("source_sndfile: upsampling %s from %d to %d\n", s->path, s->input_samplerate, s->samplerate); } + s->duration = (uint_t)FLOOR(s->duration * s->ratio); } #else if (s->ratio != 1) { @@ -138,7 +156,7 @@ aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * path, uint_t samplera /* allocate data for de/interleaving reallocated when needed. */ s->scratch_size = s->input_hop_size * s->input_channels; - s->scratch_data = AUBIO_ARRAY(float,s->scratch_size); + s->scratch_data = AUBIO_ARRAY(smpl_t, s->scratch_size); return s; @@ -152,7 +170,7 @@ beach: void aubio_source_sndfile_do(aubio_source_sndfile_t * s, fvec_t * read_data, uint_t * read){ uint_t i,j, input_channels = s->input_channels; /* read from file into scratch_data */ - sf_count_t read_samples = sf_read_float (s->handle, s->scratch_data, s->scratch_size); + sf_count_t read_samples = aubio_sf_read_smpl (s->handle, s->scratch_data, s->scratch_size); /* where to store de-interleaved data */ smpl_t *ptr_data; @@ -175,8 +193,8 @@ void aubio_source_sndfile_do(aubio_source_sndfile_t * s, fvec_t * read_data, uin } #ifdef HAVE_SAMPLERATE - if (s->resampler) { - aubio_resampler_do(s->resampler, s->input_data, read_data); + if (s->resamplers) { + aubio_resampler_do(s->resamplers[0], s->input_data, read_data); } #endif /* HAVE_SAMPLERATE */ @@ -193,15 +211,13 @@ void aubio_source_sndfile_do(aubio_source_sndfile_t * s, fvec_t * read_data, uin void aubio_source_sndfile_do_multi(aubio_source_sndfile_t * s, fmat_t * read_data, uint_t * read){ uint_t i,j, input_channels = s->input_channels; /* do actual reading */ - sf_count_t read_samples = sf_read_float (s->handle, s->scratch_data, s->scratch_size); + sf_count_t read_samples = aubio_sf_read_smpl (s->handle, s->scratch_data, s->scratch_size); /* where to store de-interleaved data */ smpl_t **ptr_data; #ifdef HAVE_SAMPLERATE if (s->ratio != 1) { - AUBIO_ERR("source_sndfile: no multi channel resampling yet\n"); - return; - //ptr_data = s->input_data->data; + ptr_data = s->input_mat->data; } else #endif /* HAVE_SAMPLERATE */ { @@ -213,7 +229,7 @@ void aubio_source_sndfile_do_multi(aubio_source_sndfile_t * s, fmat_t * read_dat // channels of the file, de-interleaving data for (j = 0; j < read_samples / input_channels; j++) { for (i = 0; i < read_data->height; i++) { - ptr_data[i][j] = (smpl_t)s->scratch_data[j * input_channels + i]; + ptr_data[i][j] = s->scratch_data[j * input_channels + i]; } } } else { @@ -221,7 +237,7 @@ void aubio_source_sndfile_do_multi(aubio_source_sndfile_t * s, fmat_t * read_dat // channel from the file to the destination matrix, de-interleaving data for (j = 0; j < read_samples / input_channels; j++) { for (i = 0; i < input_channels; i++) { - ptr_data[i][j] = (smpl_t)s->scratch_data[j * input_channels + i]; + ptr_data[i][j] = s->scratch_data[j * input_channels + i]; } } } @@ -231,14 +247,21 @@ void aubio_source_sndfile_do_multi(aubio_source_sndfile_t * s, fmat_t * read_dat // of the file to each additional channels, de-interleaving data for (j = 0; j < read_samples / input_channels; j++) { for (i = input_channels; i < read_data->height; i++) { - ptr_data[i][j] = (smpl_t)s->scratch_data[j * input_channels + (input_channels - 1)]; + ptr_data[i][j] = s->scratch_data[j * input_channels + (input_channels - 1)]; } } } #ifdef HAVE_SAMPLERATE - if (s->resampler) { - //aubio_resampler_do(s->resampler, s->input_data, read_data); + if (s->resamplers) { + for (i = 0; i < input_channels; i++) { + fvec_t input_chan, read_chan; + input_chan.data = s->input_mat->data[i]; + input_chan.length = s->input_mat->length; + read_chan.data = read_data->data[i]; + read_chan.length = read_data->length; + aubio_resampler_do(s->resamplers[i], &input_chan, &read_chan); + } } #endif /* HAVE_SAMPLERATE */ @@ -262,9 +285,27 @@ uint_t aubio_source_sndfile_get_channels(aubio_source_sndfile_t * s) { return s->input_channels; } +uint_t aubio_source_sndfile_get_duration (const aubio_source_sndfile_t * s) { + if (s && s->duration) { + return s->duration; + } + return 0; +} + uint_t aubio_source_sndfile_seek (aubio_source_sndfile_t * s, uint_t pos) { uint_t resampled_pos = (uint_t)ROUND(pos / s->ratio); - sf_count_t sf_ret = sf_seek (s->handle, resampled_pos, SEEK_SET); + sf_count_t sf_ret; + if (s->handle == NULL) { + AUBIO_ERR("source_sndfile: failed seeking in %s (file not opened?)\n", + s->path); + return AUBIO_FAIL; + } + if ((sint_t)pos < 0) { + AUBIO_ERR("source_sndfile: could not seek %s at %d (seeking position" + " should be >= 0)\n", s->path, pos); + return AUBIO_FAIL; + } + sf_ret = sf_seek (s->handle, resampled_pos, SEEK_SET); if (sf_ret == -1) { AUBIO_ERR("source_sndfile: Failed seeking %s at %d: %s\n", s->path, pos, sf_strerror (NULL)); return AUBIO_FAIL; @@ -279,12 +320,13 @@ uint_t aubio_source_sndfile_seek (aubio_source_sndfile_t * s, uint_t pos) { uint_t aubio_source_sndfile_close (aubio_source_sndfile_t *s) { if (!s->handle) { - return AUBIO_FAIL; + return AUBIO_OK; } if(sf_close(s->handle)) { AUBIO_ERR("source_sndfile: Error closing file %s: %s\n", s->path, sf_strerror (NULL)); return AUBIO_FAIL; } + s->handle = NULL; return AUBIO_OK; } @@ -292,13 +334,23 @@ void del_aubio_source_sndfile(aubio_source_sndfile_t * s){ if (!s) return; aubio_source_sndfile_close(s); #ifdef HAVE_SAMPLERATE - if (s->resampler != NULL) { - del_aubio_resampler(s->resampler); + if (s->resamplers != NULL) { + uint_t i = 0, input_channels = s->input_channels; + for (i = 0; i < input_channels; i ++) { + if (s->resamplers[i] != NULL) { + del_aubio_resampler(s->resamplers[i]); + } + } + AUBIO_FREE(s->resamplers); } if (s->input_data) { del_fvec(s->input_data); } + if (s->input_mat) { + del_fmat(s->input_mat); + } #endif /* HAVE_SAMPLERATE */ + if (s->path) AUBIO_FREE(s->path); AUBIO_FREE(s->scratch_data); AUBIO_FREE(s); } diff --git a/src/io/source_sndfile.h b/src/io/source_sndfile.h index 54a37afe..210a40d2 100644 --- a/src/io/source_sndfile.h +++ b/src/io/source_sndfile.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SOURCE_SNDFILE_H -#define _AUBIO_SOURCE_SNDFILE_H +#ifndef AUBIO_SOURCE_SNDFILE_H +#define AUBIO_SOURCE_SNDFILE_H /** \file @@ -56,7 +56,7 @@ typedef struct _aubio_source_sndfile_t aubio_source_sndfile_t; ::aubio_source_sndfile_get_samplerate. */ -aubio_source_sndfile_t * new_aubio_source_sndfile(char_t * uri, uint_t samplerate, uint_t hop_size); +aubio_source_sndfile_t * new_aubio_source_sndfile(const char_t * uri, uint_t samplerate, uint_t hop_size); /** @@ -120,6 +120,16 @@ uint_t aubio_source_sndfile_seek (aubio_source_sndfile_t *s, uint_t pos); /** + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_sndfile + \return number of frames in file + +*/ +uint_t aubio_source_sndfile_get_duration (const aubio_source_sndfile_t *s); + +/** + close source \param s source object, created with ::new_aubio_source_sndfile @@ -142,4 +152,4 @@ void del_aubio_source_sndfile(aubio_source_sndfile_t * s); } #endif -#endif /* _AUBIO_SOURCE_SNDFILE_H */ +#endif /* AUBIO_SOURCE_SNDFILE_H */ diff --git a/src/io/source_wavread.c b/src/io/source_wavread.c index e93bb025..640201bb 100644 --- a/src/io/source_wavread.c +++ b/src/io/source_wavread.c @@ -18,11 +18,10 @@ */ -#include "config.h" +#include "aubio_priv.h" #ifdef HAVE_WAVREAD -#include "aubio_priv.h" #include "fvec.h" #include "fmat.h" #include "source_wavread.h" @@ -52,14 +51,20 @@ struct _aubio_source_wavread_t { uint_t read_index; uint_t eof; + uint_t duration; + size_t seek_start; unsigned char *short_output; fmat_t *output; }; -unsigned int read_little_endian (unsigned char *buf, unsigned int length); -unsigned int read_little_endian (unsigned char *buf, unsigned int length) { +static unsigned int read_little_endian (unsigned char *buf, + unsigned int length); + +static unsigned int read_little_endian (unsigned char *buf, + unsigned int length) +{ uint_t i, ret = 0; for (i = 0; i < length; i++) { ret += buf[i] << (i * 8); @@ -67,11 +72,11 @@ unsigned int read_little_endian (unsigned char *buf, unsigned int length) { return ret; } -aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplerate, uint_t hop_size) { +aubio_source_wavread_t * new_aubio_source_wavread(const char_t * path, uint_t samplerate, uint_t hop_size) { aubio_source_wavread_t * s = AUBIO_NEW(aubio_source_wavread_t); - size_t bytes_read = 0, bytes_expected = 44; - unsigned char buf[5]; - unsigned int format, channels, sr, byterate, blockalign, bitspersample;//, data_size; + size_t bytes_read = 0, bytes_junk = 0, bytes_expected = 44; + unsigned char buf[5] = "\0"; + unsigned int format, channels, sr, byterate, blockalign, duration, bitspersample;//, data_size; if (path == NULL) { AUBIO_ERR("source_wavread: Aborted opening null path\n"); @@ -86,7 +91,10 @@ aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplera goto beach; } - s->path = path; + if (s->path) AUBIO_FREE(s->path); + s->path = AUBIO_ARRAY(char_t, strnlen(path, PATH_MAX) + 1); + strncpy(s->path, path, strnlen(path, PATH_MAX) + 1); + s->samplerate = samplerate; s->hop_size = hop_size; @@ -100,7 +108,7 @@ aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplera bytes_read += fread(buf, 1, 4, s->fid); buf[4] = '\0'; if ( strcmp((const char *)buf, "RIFF") != 0 ) { - AUBIO_ERR("source_wavread: could not find RIFF header in %s\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (could not find RIFF header)\n", s->path); goto beach; } @@ -111,15 +119,34 @@ aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplera bytes_read += fread(buf, 1, 4, s->fid); buf[4] = '\0'; if ( strcmp((const char *)buf, "WAVE") != 0 ) { - AUBIO_ERR("source_wavread: wrong format in RIFF header in %s\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (wrong format in RIFF header)\n", s->path); goto beach; } // Subchunk1ID bytes_read += fread(buf, 1, 4, s->fid); buf[4] = '\0'; + + // check if we have a JUNK Chunk + if ( strcmp((const char *)buf, "JUNK") == 0 ) { + bytes_junk = fread(buf, 1, 4, s->fid); + buf[4] = '\0'; + bytes_junk += read_little_endian(buf, 4); + if (fseek(s->fid, bytes_read + bytes_junk, SEEK_SET) != 0) { + AUBIO_ERR("source_wavread: Failed opening %s (could not seek past JUNK Chunk: %s)\n", + s->path, strerror(errno)); + goto beach; + } + bytes_read += bytes_junk; + bytes_expected += bytes_junk + 4; + // now really read the fmt chunk + bytes_read += fread(buf, 1, 4, s->fid); + buf[4] = '\0'; + } + + // get the fmt chunk if ( strcmp((const char *)buf, "fmt ") != 0 ) { - AUBIO_ERR("source_wavread: fmt RIFF header in %s\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (could not find 'fmt ' in RIFF header)\n", s->path); goto beach; } @@ -128,18 +155,18 @@ aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplera format = read_little_endian(buf, 4); if ( format != 16 ) { // TODO accept format 18 - AUBIO_ERR("source_wavread: file %s is not encoded with PCM\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (not encoded with PCM)\n", s->path); goto beach; } if ( buf[1] || buf[2] | buf[3] ) { - AUBIO_ERR("source_wavread: Subchunk1Size should be 0, in %s\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (Subchunk1Size should be 0)\n", s->path); goto beach; } // AudioFormat bytes_read += fread(buf, 1, 2, s->fid); if ( buf[0] != 1 || buf[1] != 0) { - AUBIO_ERR("source_wavread: AudioFormat should be PCM, in %s\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (AudioFormat should be PCM)\n", s->path); goto beach; } @@ -171,12 +198,12 @@ aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplera #endif if ( byterate * 8 != sr * channels * bitspersample ) { - AUBIO_ERR("source_wavread: wrong byterate in %s\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (wrong byterate)\n", s->path); goto beach; } if ( blockalign * 8 != channels * bitspersample ) { - AUBIO_ERR("source_wavread: wrong blockalign in %s\n", s->path); + AUBIO_ERR("source_wavread: Failed opening %s (wrong blockalign)\n", s->path); goto beach; } @@ -205,13 +232,29 @@ aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplera // Subchunk2ID bytes_read += fread(buf, 1, 4, s->fid); buf[4] = '\0'; - if ( strcmp((const char *)buf, "data") != 0 ) { - AUBIO_ERR("source_wavread: data RIFF header not found in %s\n", s->path); - goto beach; + while ( strcmp((const char *)buf, "data") != 0 ) { + if (feof(s->fid) || ferror(s->fid)) { + AUBIO_ERR("source_wavread: no data RIFF header found in %s\n", s->path); + goto beach; + } + bytes_junk = fread(buf, 1, 4, s->fid); + buf[4] = '\0'; + bytes_junk += read_little_endian(buf, 4); + if (fseek(s->fid, bytes_read + bytes_junk, SEEK_SET) != 0) { + AUBIO_ERR("source_wavread: could not seek past unknown chunk in %s (%s)\n", + s->path, strerror(errno)); + goto beach; + } + bytes_read += bytes_junk; + bytes_expected += bytes_junk+ 4; + bytes_read += fread(buf, 1, 4, s->fid); + buf[4] = '\0'; } // Subchunk2Size bytes_read += fread(buf, 1, 4, s->fid); + duration = read_little_endian(buf, 4) / blockalign; + //data_size = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); //AUBIO_MSG("found %d frames in %s\n", 8 * data_size / bitspersample / channels, s->path); @@ -232,6 +275,8 @@ aubio_source_wavread_t * new_aubio_source_wavread(char_t * path, uint_t samplera s->blockalign= blockalign; s->bitspersample = bitspersample; + s->duration = duration; + s->short_output = (unsigned char *)calloc(s->blockalign, AUBIO_WAVREAD_BUFSIZE); s->read_index = 0; s->read_samples = 0; @@ -283,6 +328,11 @@ void aubio_source_wavread_do(aubio_source_wavread_t * s, fvec_t * read_data, uin uint_t i, j; uint_t end = 0; uint_t total_wrote = 0; + if (s->fid == NULL) { + AUBIO_ERR("source_wavread: could not read from %s (file not opened)\n", + s->path); + return; + } while (total_wrote < s->hop_size) { end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote); for (i = 0; i < end; i++) { @@ -317,6 +367,11 @@ void aubio_source_wavread_do_multi(aubio_source_wavread_t * s, fmat_t * read_dat uint_t i,j; uint_t end = 0; uint_t total_wrote = 0; + if (s->fid == NULL) { + AUBIO_ERR("source_wavread: could not read from %s (file not opened)\n", + s->path); + return; + } while (total_wrote < s->hop_size) { end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote); for (j = 0; j < read_data->height; j++) { @@ -357,7 +412,12 @@ uint_t aubio_source_wavread_get_channels(aubio_source_wavread_t * s) { uint_t aubio_source_wavread_seek (aubio_source_wavread_t * s, uint_t pos) { uint_t ret = 0; + if (s->fid == NULL) { + AUBIO_ERR("source_wavread: could not seek %s (file not opened?)\n", s->path, pos); + return AUBIO_FAIL; + } if ((sint_t)pos < 0) { + AUBIO_ERR("source_wavread: could not seek %s at %d (seeking position should be >= 0)\n", s->path, pos); return AUBIO_FAIL; } ret = fseek(s->fid, s->seek_start + pos * s->blockalign, SEEK_SET); @@ -371,9 +431,16 @@ uint_t aubio_source_wavread_seek (aubio_source_wavread_t * s, uint_t pos) { return AUBIO_OK; } +uint_t aubio_source_wavread_get_duration (const aubio_source_wavread_t * s) { + if (s && s->duration) { + return s->duration; + } + return 0; +} + uint_t aubio_source_wavread_close (aubio_source_wavread_t * s) { - if (!s->fid) { - return AUBIO_FAIL; + if (s->fid == NULL) { + return AUBIO_OK; } if (fclose(s->fid)) { AUBIO_ERR("source_wavread: could not close %s (%s)\n", s->path, strerror(errno)); @@ -388,6 +455,7 @@ void del_aubio_source_wavread(aubio_source_wavread_t * s) { aubio_source_wavread_close(s); if (s->short_output) AUBIO_FREE(s->short_output); if (s->output) del_fmat(s->output); + if (s->path) AUBIO_FREE(s->path); AUBIO_FREE(s); } diff --git a/src/io/source_wavread.h b/src/io/source_wavread.h index efdb88d4..65f752f9 100644 --- a/src/io/source_wavread.h +++ b/src/io/source_wavread.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SOURCE_WAVREAD_H -#define _AUBIO_SOURCE_WAVREAD_H +#ifndef AUBIO_SOURCE_WAVREAD_H +#define AUBIO_SOURCE_WAVREAD_H /** \file @@ -61,7 +61,7 @@ typedef struct _aubio_source_wavread_t aubio_source_wavread_t; ::aubio_source_wavread_get_samplerate. */ -aubio_source_wavread_t * new_aubio_source_wavread(char_t * uri, uint_t samplerate, uint_t hop_size); +aubio_source_wavread_t * new_aubio_source_wavread(const char_t * uri, uint_t samplerate, uint_t hop_size); /** @@ -125,6 +125,16 @@ uint_t aubio_source_wavread_seek (aubio_source_wavread_t *s, uint_t pos); /** + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_sndfile + \return number of frames in file + +*/ +uint_t aubio_source_wavread_get_duration (const aubio_source_wavread_t *s); + +/** + close source \param s source object, created with ::new_aubio_source_wavread @@ -147,4 +157,4 @@ void del_aubio_source_wavread(aubio_source_wavread_t * s); } #endif -#endif /* _AUBIO_SOURCE_WAVREAD_H */ +#endif /* AUBIO_SOURCE_WAVREAD_H */ diff --git a/src/io/utils_apple_audio.c b/src/io/utils_apple_audio.c index 2e651d50..34c35784 100644 --- a/src/io/utils_apple_audio.c +++ b/src/io/utils_apple_audio.c @@ -1,4 +1,4 @@ -#include "config.h" +#include "aubio_priv.h" #if defined(HAVE_SOURCE_APPLE_AUDIO) || defined(HAVE_SINK_APPLE_AUDIO) @@ -6,7 +6,6 @@ #include // ExtAudioFileRef, AudioStreamBasicDescription, AudioBufferList, ... #include -#include "aubio_priv.h" int createAubioBufferList(AudioBufferList *bufferList, int channels, int segmentSize); void freeAudioBufferList(AudioBufferList *bufferList); diff --git a/src/lvec.c b/src/lvec.c index 07423fbc..dd63bf42 100644 --- a/src/lvec.c +++ b/src/lvec.c @@ -21,7 +21,7 @@ #include "aubio_priv.h" #include "lvec.h" -lvec_t * new_lvec( uint_t length) { +lvec_t * new_lvec(uint_t length) { lvec_t * s; if ((sint_t)length <= 0) { return NULL; @@ -45,13 +45,13 @@ lsmp_t lvec_get_sample(lvec_t *s, uint_t position) { return s->data[position]; } -lsmp_t * lvec_get_data(lvec_t *s) { +lsmp_t * lvec_get_data(const lvec_t *s) { return s->data; } /* helper functions */ -void lvec_print(lvec_t *s) { +void lvec_print(const lvec_t *s) { uint_t j; for (j=0; j< s->length; j++) { AUBIO_MSG(AUBIO_LSMP_FMT " ", s->data[j]); diff --git a/src/lvec.h b/src/lvec.h index fc261be5..402ba0ff 100644 --- a/src/lvec.h +++ b/src/lvec.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO__LVEC_H -#define _AUBIO__LVEC_H +#ifndef AUBIO_LVEC_H +#define AUBIO_LVEC_H #ifdef __cplusplus extern "C" { @@ -80,14 +80,14 @@ void lvec_set_sample(lvec_t *s, lsmp_t data, uint_t position); \param s vector to read from */ -lsmp_t * lvec_get_data(lvec_t *s); +lsmp_t * lvec_get_data(const lvec_t *s); /** print out lvec data \param s vector to print out */ -void lvec_print(lvec_t *s); +void lvec_print(const lvec_t *s); /** set all elements to a given value @@ -115,4 +115,4 @@ void lvec_ones(lvec_t *s); } #endif -#endif /* _AUBIO__LVEC_H */ +#endif /* AUBIO_LVEC_H */ diff --git a/src/mathutils.c b/src/mathutils.c index a1035173..45f61c93 100644 --- a/src/mathutils.c +++ b/src/mathutils.c @@ -24,7 +24,6 @@ #include "fvec.h" #include "mathutils.h" #include "musicutils.h" -#include "config.h" /** Window types */ typedef enum @@ -253,19 +252,45 @@ fvec_max_elem (fvec_t * s) void fvec_shift (fvec_t * s) { + uint_t half = s->length / 2, start = half, j; + // if length is odd, middle element is moved to the end + if (2 * half < s->length) start ++; #ifndef HAVE_ATLAS - uint_t j; - for (j = 0; j < s->length / 2; j++) { - ELEM_SWAP (s->data[j], s->data[j + s->length / 2]); + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[j], s->data[j + start]); } #else - uint_t half = s->length / 2; - aubio_cblas_swap(half, s->data, 1, s->data + half, 1); + aubio_cblas_swap(half, s->data, 1, s->data + start, 1); #endif + if (start != half) { + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[j + start - 1], s->data[j + start]); + } + } +} + +void +fvec_ishift (fvec_t * s) +{ + uint_t half = s->length / 2, start = half, j; + // if length is odd, middle element is moved to the beginning + if (2 * half < s->length) start ++; +#ifndef HAVE_ATLAS + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[j], s->data[j + start]); + } +#else + aubio_cblas_swap(half, s->data, 1, s->data + start, 1); +#endif + if (start != half) { + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[half], s->data[j]); + } + } } smpl_t -aubio_level_lin (fvec_t * f) +aubio_level_lin (const fvec_t * f) { smpl_t energy = 0.; #ifndef HAVE_ATLAS @@ -414,8 +439,9 @@ smpl_t fvec_median (fvec_t * input) { } } -smpl_t fvec_quadratic_peak_pos (fvec_t * x, uint_t pos) { +smpl_t fvec_quadratic_peak_pos (const fvec_t * x, uint_t pos) { smpl_t s0, s1, s2; uint_t x0, x2; + smpl_t half = .5, two = 2.; if (pos == 0 || pos == x->length - 1) return pos; x0 = (pos < 1) ? pos : pos - 1; x2 = (pos + 1 < x->length) ? pos + 1 : pos; @@ -424,7 +450,7 @@ smpl_t fvec_quadratic_peak_pos (fvec_t * x, uint_t pos) { s0 = x->data[x0]; s1 = x->data[pos]; s2 = x->data[x2]; - return pos + 0.5 * (s0 - s2 ) / (s0 - 2.* s1 + s2); + return pos + half * (s0 - s2 ) / (s0 - two * s1 + s2); } smpl_t fvec_quadratic_peak_mag (fvec_t *x, smpl_t pos) { @@ -438,7 +464,7 @@ smpl_t fvec_quadratic_peak_mag (fvec_t *x, smpl_t pos) { return x1 - .25 * (x0 - x2) * (pos - index); } -uint_t fvec_peakpick(fvec_t * onset, uint_t pos) { +uint_t fvec_peakpick(const fvec_t * onset, uint_t pos) { uint_t tmp=0; tmp = (onset->data[pos] > onset->data[pos-1] && onset->data[pos] > onset->data[pos+1] @@ -525,19 +551,19 @@ aubio_next_power_of_two (uint_t a) } smpl_t -aubio_db_spl (fvec_t * o) +aubio_db_spl (const fvec_t * o) { return 10. * LOG10 (aubio_level_lin (o)); } uint_t -aubio_silence_detection (fvec_t * o, smpl_t threshold) +aubio_silence_detection (const fvec_t * o, smpl_t threshold) { return (aubio_db_spl (o) < threshold); } smpl_t -aubio_level_detection (fvec_t * o, smpl_t threshold) +aubio_level_detection (const fvec_t * o, smpl_t threshold) { smpl_t db_spl = aubio_db_spl (o); if (db_spl < threshold) { @@ -571,7 +597,7 @@ aubio_zero_crossing_rate (fvec_t * input) } void -aubio_autocorr (fvec_t * input, fvec_t * output) +aubio_autocorr (const fvec_t * input, fvec_t * output) { uint_t i, j, length = input->length; smpl_t *data, *acf; diff --git a/src/mathutils.h b/src/mathutils.h index 77cc7d50..6638f692 100644 --- a/src/mathutils.h +++ b/src/mathutils.h @@ -27,8 +27,8 @@ */ -#ifndef _AUBIO_MATHUTILS_H -#define _AUBIO_MATHUTILS_H +#ifndef AUBIO_MATHUTILS_H +#define AUBIO_MATHUTILS_H #include "fvec.h" #include "musicutils.h" @@ -99,6 +99,24 @@ resulting spectrum. See Amalia de Götzen's paper referred to above. */ void fvec_shift (fvec_t * v); +/** swap the left and right halves of a vector + + This function swaps the left part of the signal with the right part of the +signal. Therefore + + \f$ a[0], a[1], ..., a[\frac{N}{2}], a[\frac{N}{2}+1], ..., a[N-1], a[N] \f$ + + becomes + + \f$ a[\frac{N}{2}+1], ..., a[N-1], a[N], a[0], a[1], ..., a[\frac{N}{2}] \f$ + + This operation, known as 'ifftshift' in the Matlab Signal Processing Toolbox, +can be used after computing the inverse FFT to simplify the phase relationship +of the resulting spectrum. See Amalia de Götzen's paper referred to above. + +*/ +void fvec_ishift (fvec_t * v); + /** compute the sum of all elements of a vector \param v vector to compute the sum of @@ -232,7 +250,7 @@ smpl_t fvec_median (fvec_t * v); \return \f$ p + p_{frac} \f$ exact peak position of interpolated maximum or minimum */ -smpl_t fvec_quadratic_peak_pos (fvec_t * x, uint_t p); +smpl_t fvec_quadratic_peak_pos (const fvec_t * x, uint_t p); /** finds magnitude of peak by quadratic interpolation @@ -275,7 +293,7 @@ peak is defined as follows: \return 1 if a peak is found, 0 otherwise */ -uint_t fvec_peakpick (fvec_t * v, uint_t p); +uint_t fvec_peakpick (const fvec_t * v, uint_t p); /** return 1 if a is a power of 2, 0 otherwise */ uint_t aubio_is_power_of_two(uint_t a); @@ -289,10 +307,10 @@ uint_t aubio_next_power_of_two(uint_t a); \param output vector to store autocorrelation function to */ -void aubio_autocorr (fvec_t * input, fvec_t * output); +void aubio_autocorr (const fvec_t * input, fvec_t * output); #ifdef __cplusplus } #endif -#endif /* _AUBIO_MATHUTILS_H */ +#endif /* AUBIO_MATHUTILS_H */ diff --git a/src/musicutils.h b/src/musicutils.h index c375b180..f71d20ba 100644 --- a/src/musicutils.h +++ b/src/musicutils.h @@ -22,8 +22,8 @@ * various functions useful in audio signal processing */ -#ifndef _AUBIO__MUSICUTILS_H -#define _AUBIO__MUSICUTILS_H +#ifndef AUBIO_MUSICUTILS_H +#define AUBIO_MUSICUTILS_H #ifdef __cplusplus extern "C" { @@ -121,7 +121,7 @@ smpl_t aubio_zero_crossing_rate (fvec_t * v); \return level of v */ -smpl_t aubio_level_lin (fvec_t * v); +smpl_t aubio_level_lin (const fvec_t * v); /** compute sound pressure level (SPL) in dB @@ -134,7 +134,7 @@ smpl_t aubio_level_lin (fvec_t * v); \return level of v in dB SPL */ -smpl_t aubio_db_spl (fvec_t * v); +smpl_t aubio_db_spl (const fvec_t * v); /** check if buffer level in dB SPL is under a given threshold @@ -144,7 +144,7 @@ smpl_t aubio_db_spl (fvec_t * v); \return 0 if level is under the given threshold, 1 otherwise */ -uint_t aubio_silence_detection (fvec_t * v, smpl_t threshold); +uint_t aubio_silence_detection (const fvec_t * v, smpl_t threshold); /** get buffer level if level >= threshold, 1. otherwise @@ -154,10 +154,10 @@ uint_t aubio_silence_detection (fvec_t * v, smpl_t threshold); \return level in dB SPL if level >= threshold, 1. otherwise */ -smpl_t aubio_level_detection (fvec_t * v, smpl_t threshold); +smpl_t aubio_level_detection (const fvec_t * v, smpl_t threshold); #ifdef __cplusplus } #endif -#endif /* _AUBIO__MUSICUTILS_H */ +#endif /* AUBIO_MUSICUTILS_H */ diff --git a/src/notes/notes.c b/src/notes/notes.c new file mode 100644 index 00000000..f6b7d567 --- /dev/null +++ b/src/notes/notes.c @@ -0,0 +1,231 @@ +/* + Copyright (C) 2014 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "pitch/pitch.h" +#include "onset/onset.h" +#include "notes/notes.h" + +#define AUBIO_DEFAULT_NOTES_SILENCE -70. +// increase to 10. for .1 cent precision +// or to 100. for .01 cent precision +#define AUBIO_DEFAULT_CENT_PRECISION 1. +#define AUBIO_DEFAULT_NOTES_MINIOI_MS 30. + +struct _aubio_notes_t { + + uint_t onset_buf_size; + uint_t pitch_buf_size; + uint_t hop_size; + + uint_t samplerate; + + uint_t median; + fvec_t *note_buffer; + fvec_t *note_buffer2; + + aubio_pitch_t *pitch; + fvec_t *pitch_output; + smpl_t pitch_tolerance; + + aubio_onset_t *onset; + fvec_t *onset_output; + smpl_t onset_threshold; + + smpl_t curnote; + smpl_t newnote; + + smpl_t silence_threshold; + + uint_t isready; +}; + +aubio_notes_t * new_aubio_notes (const char_t * method, + uint_t buf_size, uint_t hop_size, uint_t samplerate) { + aubio_notes_t *o = AUBIO_NEW(aubio_notes_t); + + const char_t * onset_method = "default"; + const char_t * pitch_method = "default"; + + o->onset_buf_size = buf_size; + o->pitch_buf_size = buf_size * 4; + o->hop_size = hop_size; + + o->onset_threshold = 0.; + o->pitch_tolerance = 0.; + + o->samplerate = samplerate; + + o->median = 6; + + o->isready = 0; + + o->onset = new_aubio_onset (onset_method, o->onset_buf_size, o->hop_size, o->samplerate); + if (o->onset_threshold != 0.) aubio_onset_set_threshold (o->onset, o->onset_threshold); + o->onset_output = new_fvec (1); + + o->pitch = new_aubio_pitch (pitch_method, o->pitch_buf_size, o->hop_size, o->samplerate); + if (o->pitch_tolerance != 0.) aubio_pitch_set_tolerance (o->pitch, o->pitch_tolerance); + aubio_pitch_set_unit (o->pitch, "midi"); + o->pitch_output = new_fvec (1); + + if (strcmp(method, "default") != 0) { + AUBIO_ERR("notes: unknown notes detection method \"%s\"\n", method); + goto fail; + } + o->note_buffer = new_fvec(o->median); + o->note_buffer2 = new_fvec(o->median); + + o->curnote = -1.; + o->newnote = 0.; + + aubio_notes_set_silence(o, AUBIO_DEFAULT_NOTES_SILENCE); + aubio_notes_set_minioi_ms (o, AUBIO_DEFAULT_NOTES_MINIOI_MS); + + return o; + +fail: + del_aubio_notes(o); + return NULL; +} + +uint_t aubio_notes_set_silence(aubio_notes_t *o, smpl_t silence) +{ + uint_t err = AUBIO_OK; + if (aubio_pitch_set_silence(o->pitch, silence) != AUBIO_OK) { + err = AUBIO_FAIL; + } + if (aubio_onset_set_silence(o->onset, silence) != AUBIO_OK) { + err = AUBIO_FAIL; + } + o->silence_threshold = silence; + return err; +} + +smpl_t aubio_notes_get_silence(const aubio_notes_t *o) +{ + return aubio_pitch_get_silence(o->pitch); +} + +uint_t aubio_notes_set_minioi_ms (aubio_notes_t *o, smpl_t minioi_ms) +{ + uint_t err = AUBIO_OK; + if (!o->onset || (aubio_onset_set_minioi_ms(o->onset, minioi_ms) != 0)) { + err = AUBIO_FAIL; + } + return err; +} + +smpl_t aubio_notes_get_minioi_ms(const aubio_notes_t *o) +{ + return aubio_onset_get_minioi_ms(o->onset); +} + +/** append new note candidate to the note_buffer and return filtered value. we + * need to copy the input array as fvec_median destroy its input data.*/ +static void +note_append (fvec_t * note_buffer, smpl_t curnote) +{ + uint_t i = 0; + for (i = 0; i < note_buffer->length - 1; i++) { + note_buffer->data[i] = note_buffer->data[i + 1]; + } + //note_buffer->data[note_buffer->length - 1] = ROUND(10.*curnote)/10.; + note_buffer->data[note_buffer->length - 1] = ROUND(AUBIO_DEFAULT_CENT_PRECISION*curnote); + return; +} + +static smpl_t +aubio_notes_get_latest_note (aubio_notes_t *o) +{ + fvec_copy(o->note_buffer, o->note_buffer2); + return fvec_median (o->note_buffer2) / AUBIO_DEFAULT_CENT_PRECISION; +} + + +void aubio_notes_do (aubio_notes_t *o, const fvec_t * input, fvec_t * notes) +{ + smpl_t new_pitch, curlevel; + fvec_zeros(notes); + aubio_onset_do(o->onset, input, o->onset_output); + + aubio_pitch_do (o->pitch, input, o->pitch_output); + new_pitch = o->pitch_output->data[0]; + if(o->median){ + note_append(o->note_buffer, new_pitch); + } + + /* curlevel is negatif or 1 if silence */ + curlevel = aubio_level_detection(input, o->silence_threshold); + if (o->onset_output->data[0] != 0) { + /* test for silence */ + if (curlevel == 1.) { + if (o->median) o->isready = 0; + /* send note off */ + //send_noteon(o->curnote,0); + //notes->data[0] = o->curnote; + //notes->data[1] = 0.; + notes->data[2] = o->curnote; + } else { + if (o->median) { + o->isready = 1; + } else { + /* kill old note */ + //send_noteon(o->curnote,0, o->samplerate); + notes->data[2] = o->curnote; + /* get and send new one */ + //send_noteon(new_pitch,127+(int)floor(curlevel), o->samplerate); + notes->data[0] = new_pitch; + notes->data[1] = 127 + (int)floor(curlevel); + o->curnote = new_pitch; + } + } + } else { + if (o->median) { + if (o->isready > 0) + o->isready++; + if (o->isready == o->median) + { + /* kill old note */ + //send_noteon(curnote,0); + notes->data[2] = o->curnote; + o->newnote = aubio_notes_get_latest_note(o); + o->curnote = o->newnote; + /* get and send new one */ + if (o->curnote>45){ + //send_noteon(curnote,127+(int)floor(curlevel)); + notes->data[0] = o->curnote; + notes->data[1] = 127 + (int) floor(curlevel); + } + } + } // if median + } +} + +void del_aubio_notes (aubio_notes_t *o) { + if (o->note_buffer) del_fvec(o->note_buffer); + if (o->note_buffer2) del_fvec(o->note_buffer2); + if (o->pitch_output) del_fvec(o->pitch_output); + if (o->pitch) del_aubio_pitch(o->pitch); + if (o->onset_output) del_fvec(o->onset_output); + if (o->onset) del_aubio_onset(o->onset); + AUBIO_FREE(o); +} diff --git a/src/notes/notes.h b/src/notes/notes.h new file mode 100644 index 00000000..bf0df85b --- /dev/null +++ b/src/notes/notes.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2003-2014 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +/** \file + + Note detection object + +*/ + +#ifndef _AUBIO_NOTES_H +#define _AUBIO_NOTES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** notes detection object */ +typedef struct _aubio_notes_t aubio_notes_t; + +/** create notes detection object + + \param method notes detection type as specified in specdesc.h + \param buf_size buffer size for phase vocoder + \param hop_size hop size for phase vocoder + \param samplerate sampling rate of the input signal + + \return newly created ::aubio_notes_t + +*/ +aubio_notes_t * new_aubio_notes (const char_t * method, + uint_t buf_size, uint_t hop_size, uint_t samplerate); + +/** delete notes detection object + + \param o notes detection object to delete + +*/ +void del_aubio_notes(aubio_notes_t * o); + +/** execute note detection on an input signal frame + + \param o note detection object as returned by new_aubio_notes() + \param input input signal of size [hop_size] + \param output output notes, fvec of length 3 + + The notes output is a vector of length 3 containing: + - 0. the midi note value, or 0 if no note was found + - 1. the note velocity + - 2. the midi note to turn off + +*/ +void aubio_notes_do (aubio_notes_t *o, const fvec_t * input, fvec_t * output); + +/** set notes detection silence threshold + + \param o notes detection object as returned by new_aubio_notes() + \param silence new silence detection threshold + + \return 0 on success, non-zero otherwise + +*/ +uint_t aubio_notes_set_silence(aubio_notes_t * o, smpl_t silence); + +/** get notes detection silence threshold + + \param o notes detection object as returned by new_aubio_notes() + + \return current silence threshold + +*/ +smpl_t aubio_notes_get_silence(const aubio_notes_t * o); + +/** get notes detection minimum inter-onset interval, in millisecond + + \param o notes detection object as returned by new_aubio_notes() + + \return current minimum inter onset interval + + */ +smpl_t aubio_notes_get_minioi_ms(const aubio_notes_t *o); + +/** set notes detection minimum inter-onset interval, in millisecond + + \param o notes detection object as returned by new_aubio_notes() + \param minioi_ms new inter-onset interval + + \return 0 on success, non-zero otherwise + +*/ +uint_t aubio_notes_set_minioi_ms (aubio_notes_t *o, smpl_t minioi_ms); + +#ifdef __cplusplus +} +#endif + +#endif /* _AUBIO_NOTES_H */ diff --git a/src/onset/onset.c b/src/onset/onset.c index f416ef89..cdd539a9 100644 --- a/src/onset/onset.c +++ b/src/onset/onset.c @@ -51,7 +51,7 @@ struct _aubio_onset_t { }; /* execute onset detection function on iput buffer */ -void aubio_onset_do (aubio_onset_t *o, fvec_t * input, fvec_t * onset) +void aubio_onset_do (aubio_onset_t *o, const fvec_t * input, fvec_t * onset) { smpl_t isonset = 0; aubio_pvoc_do (o->pv,input, o->fftgrain); @@ -99,17 +99,17 @@ void aubio_onset_do (aubio_onset_t *o, fvec_t * input, fvec_t * onset) return; } -uint_t aubio_onset_get_last (aubio_onset_t *o) +uint_t aubio_onset_get_last (const aubio_onset_t *o) { return o->last_onset - o->delay; } -smpl_t aubio_onset_get_last_s (aubio_onset_t *o) +smpl_t aubio_onset_get_last_s (const aubio_onset_t *o) { return aubio_onset_get_last (o) / (smpl_t) (o->samplerate); } -smpl_t aubio_onset_get_last_ms (aubio_onset_t *o) +smpl_t aubio_onset_get_last_ms (const aubio_onset_t *o) { return aubio_onset_get_last_s (o) * 1000.; } @@ -130,7 +130,7 @@ uint_t aubio_onset_set_silence(aubio_onset_t * o, smpl_t silence) { return AUBIO_OK; } -smpl_t aubio_onset_get_silence(aubio_onset_t * o) { +smpl_t aubio_onset_get_silence(const aubio_onset_t * o) { return o->silence; } @@ -139,7 +139,7 @@ uint_t aubio_onset_set_threshold(aubio_onset_t * o, smpl_t threshold) { return AUBIO_OK; } -smpl_t aubio_onset_get_threshold(aubio_onset_t * o) { +smpl_t aubio_onset_get_threshold(const aubio_onset_t * o) { return aubio_peakpicker_get_threshold(o->pp); } @@ -148,15 +148,15 @@ uint_t aubio_onset_set_minioi(aubio_onset_t * o, uint_t minioi) { return AUBIO_OK; } -uint_t aubio_onset_get_minioi(aubio_onset_t * o) { +uint_t aubio_onset_get_minioi(const aubio_onset_t * o) { return o->minioi; } uint_t aubio_onset_set_minioi_s(aubio_onset_t * o, smpl_t minioi) { - return aubio_onset_set_minioi (o, minioi * o->samplerate); + return aubio_onset_set_minioi (o, (uint_t)ROUND(minioi * o->samplerate)); } -smpl_t aubio_onset_get_minioi_s(aubio_onset_t * o) { +smpl_t aubio_onset_get_minioi_s(const aubio_onset_t * o) { return aubio_onset_get_minioi (o) / (smpl_t) o->samplerate; } @@ -164,7 +164,7 @@ uint_t aubio_onset_set_minioi_ms(aubio_onset_t * o, smpl_t minioi) { return aubio_onset_set_minioi_s (o, minioi / 1000.); } -smpl_t aubio_onset_get_minioi_ms(aubio_onset_t * o) { +smpl_t aubio_onset_get_minioi_ms(const aubio_onset_t * o) { return aubio_onset_get_minioi_s (o) * 1000.; } @@ -173,7 +173,7 @@ uint_t aubio_onset_set_delay(aubio_onset_t * o, uint_t delay) { return AUBIO_OK; } -uint_t aubio_onset_get_delay(aubio_onset_t * o) { +uint_t aubio_onset_get_delay(const aubio_onset_t * o) { return o->delay; } @@ -181,7 +181,7 @@ uint_t aubio_onset_set_delay_s(aubio_onset_t * o, smpl_t delay) { return aubio_onset_set_delay (o, delay * o->samplerate); } -smpl_t aubio_onset_get_delay_s(aubio_onset_t * o) { +smpl_t aubio_onset_get_delay_s(const aubio_onset_t * o) { return aubio_onset_get_delay (o) / (smpl_t) o->samplerate; } @@ -189,21 +189,21 @@ uint_t aubio_onset_set_delay_ms(aubio_onset_t * o, smpl_t delay) { return aubio_onset_set_delay_s (o, delay / 1000.); } -smpl_t aubio_onset_get_delay_ms(aubio_onset_t * o) { +smpl_t aubio_onset_get_delay_ms(const aubio_onset_t * o) { return aubio_onset_get_delay_s (o) * 1000.; } -smpl_t aubio_onset_get_descriptor(aubio_onset_t * o) { +smpl_t aubio_onset_get_descriptor(const aubio_onset_t * o) { return o->desc->data[0]; } -smpl_t aubio_onset_get_thresholded_descriptor(aubio_onset_t * o) { +smpl_t aubio_onset_get_thresholded_descriptor(const aubio_onset_t * o) { fvec_t * thresholded = aubio_peakpicker_get_thresholded_input(o->pp); return thresholded->data[0]; } /* Allocate memory for an onset detection */ -aubio_onset_t * new_aubio_onset (char_t * onset_mode, +aubio_onset_t * new_aubio_onset (const char_t * onset_mode, uint_t buf_size, uint_t hop_size, uint_t samplerate) { aubio_onset_t * o = AUBIO_NEW(aubio_onset_t); @@ -212,11 +212,11 @@ aubio_onset_t * new_aubio_onset (char_t * onset_mode, if ((sint_t)hop_size < 1) { AUBIO_ERR("onset: got hop_size %d, but can not be < 1\n", hop_size); goto beach; - } else if ((sint_t)buf_size < 1) { - AUBIO_ERR("onset: got buffer_size %d, but can not be < 1\n", buf_size); + } else if ((sint_t)buf_size < 2) { + AUBIO_ERR("onset: got buffer_size %d, but can not be < 2\n", buf_size); goto beach; } else if (buf_size < hop_size) { - AUBIO_ERR("onset: hop size (%d) is larger than win size (%d)\n", buf_size, hop_size); + AUBIO_ERR("onset: hop size (%d) is larger than win size (%d)\n", hop_size, buf_size); goto beach; } else if ((sint_t)samplerate < 1) { AUBIO_ERR("onset: samplerate (%d) can not be < 1\n", samplerate); @@ -231,6 +231,7 @@ aubio_onset_t * new_aubio_onset (char_t * onset_mode, o->pv = new_aubio_pvoc(buf_size, o->hop_size); o->pp = new_aubio_peakpicker(); o->od = new_aubio_specdesc(onset_mode,buf_size); + if (o->od == NULL) goto beach_specdesc; o->fftgrain = new_cvec(buf_size); o->desc = new_fvec(1); @@ -243,6 +244,9 @@ aubio_onset_t * new_aubio_onset (char_t * onset_mode, o->total_frames = 0; return o; +beach_specdesc: + del_aubio_peakpicker(o->pp); + del_aubio_pvoc(o->pv); beach: AUBIO_FREE(o); return NULL; diff --git a/src/onset/onset.h b/src/onset/onset.h index 984d35cf..17d061c5 100644 --- a/src/onset/onset.h +++ b/src/onset/onset.h @@ -39,8 +39,8 @@ */ -#ifndef _AUBIO_ONSET_H -#define _AUBIO_ONSET_H +#ifndef AUBIO_ONSET_H +#define AUBIO_ONSET_H #ifdef __cplusplus extern "C" { @@ -59,7 +59,7 @@ typedef struct _aubio_onset_t aubio_onset_t; \return newly created ::aubio_onset_t */ -aubio_onset_t * new_aubio_onset (char_t * method, +aubio_onset_t * new_aubio_onset (const char_t * method, uint_t buf_size, uint_t hop_size, uint_t samplerate); /** execute onset detection @@ -88,7 +88,7 @@ aubio_onset_t * new_aubio_onset (char_t * method, aubio_onset_get_delay(). */ -void aubio_onset_do (aubio_onset_t *o, fvec_t * input, fvec_t * onset); +void aubio_onset_do (aubio_onset_t *o, const fvec_t * input, fvec_t * onset); /** get the time of the latest onset detected, in samples @@ -97,7 +97,7 @@ void aubio_onset_do (aubio_onset_t *o, fvec_t * input, fvec_t * onset); \return onset detection timestamps (in samples) */ -uint_t aubio_onset_get_last (aubio_onset_t *o); +uint_t aubio_onset_get_last (const aubio_onset_t *o); /** get the time of the latest onset detected, in seconds @@ -106,7 +106,7 @@ uint_t aubio_onset_get_last (aubio_onset_t *o); \return onset detection timestamps (in seconds) */ -smpl_t aubio_onset_get_last_s (aubio_onset_t *o); +smpl_t aubio_onset_get_last_s (const aubio_onset_t *o); /** get the time of the latest onset detected, in milliseconds @@ -115,7 +115,7 @@ smpl_t aubio_onset_get_last_s (aubio_onset_t *o); \return onset detection timestamps (in milliseconds) */ -smpl_t aubio_onset_get_last_ms (aubio_onset_t *o); +smpl_t aubio_onset_get_last_ms (const aubio_onset_t *o); /** set onset detection adaptive whitening @@ -149,7 +149,7 @@ uint_t aubio_onset_set_silence(aubio_onset_t * o, smpl_t silence); \return current silence threshold */ -smpl_t aubio_onset_get_silence(aubio_onset_t * o); +smpl_t aubio_onset_get_silence(const aubio_onset_t * o); /** get onset detection function @@ -157,7 +157,7 @@ smpl_t aubio_onset_get_silence(aubio_onset_t * o); \return the current value of the descriptor */ -smpl_t aubio_onset_get_descriptor ( aubio_onset_t *o); +smpl_t aubio_onset_get_descriptor (const aubio_onset_t *o); /** get thresholded onset detection function @@ -165,7 +165,7 @@ smpl_t aubio_onset_get_descriptor ( aubio_onset_t *o); \return the value of the thresholded descriptor */ -smpl_t aubio_onset_get_thresholded_descriptor ( aubio_onset_t *o); +smpl_t aubio_onset_get_thresholded_descriptor (const aubio_onset_t *o); /** set onset detection peak picking threshold @@ -236,7 +236,7 @@ uint_t aubio_onset_set_delay_ms(aubio_onset_t * o, smpl_t delay); samples) */ -uint_t aubio_onset_get_minioi(aubio_onset_t * o); +uint_t aubio_onset_get_minioi(const aubio_onset_t * o); /** get minimum inter onset interval in seconds @@ -245,7 +245,7 @@ uint_t aubio_onset_get_minioi(aubio_onset_t * o); seconds) */ -smpl_t aubio_onset_get_minioi_s(aubio_onset_t * o); +smpl_t aubio_onset_get_minioi_s(const aubio_onset_t * o); /** get minimum inter onset interval in milliseconds @@ -254,7 +254,7 @@ smpl_t aubio_onset_get_minioi_s(aubio_onset_t * o); milliseconds) */ -smpl_t aubio_onset_get_minioi_ms(aubio_onset_t * o); +smpl_t aubio_onset_get_minioi_ms(const aubio_onset_t * o); /** get delay in samples @@ -263,7 +263,7 @@ smpl_t aubio_onset_get_minioi_ms(aubio_onset_t * o); (in samples) */ -uint_t aubio_onset_get_delay(aubio_onset_t * o); +uint_t aubio_onset_get_delay(const aubio_onset_t * o); /** get delay in seconds @@ -272,7 +272,7 @@ uint_t aubio_onset_get_delay(aubio_onset_t * o); (in seconds) */ -smpl_t aubio_onset_get_delay_s(aubio_onset_t * o); +smpl_t aubio_onset_get_delay_s(const aubio_onset_t * o); /** get delay in milliseconds @@ -281,7 +281,7 @@ smpl_t aubio_onset_get_delay_s(aubio_onset_t * o); (in milliseconds) */ -smpl_t aubio_onset_get_delay_ms(aubio_onset_t * o); +smpl_t aubio_onset_get_delay_ms(const aubio_onset_t * o); /** get onset peak picking threshold @@ -289,7 +289,7 @@ smpl_t aubio_onset_get_delay_ms(aubio_onset_t * o); \return current onset detection threshold */ -smpl_t aubio_onset_get_threshold(aubio_onset_t * o); +smpl_t aubio_onset_get_threshold(const aubio_onset_t * o); /** delete onset detection object @@ -302,4 +302,4 @@ void del_aubio_onset(aubio_onset_t * o); } #endif -#endif /* _AUBIO_ONSET_H */ +#endif /* AUBIO_ONSET_H */ diff --git a/src/onset/peakpicker.c b/src/onset/peakpicker.c index 0d83becf..c47d0496 100644 --- a/src/onset/peakpicker.c +++ b/src/onset/peakpicker.c @@ -185,7 +185,9 @@ new_aubio_peakpicker (void) generated with octave butter function: [b,a] = butter(2, 0.34); */ t->biquad = new_aubio_filter_biquad (0.15998789, 0.31997577, 0.15998789, - -0.59488894, 0.23484048); + // FIXME: broken since c9e20ca, revert for now + //-0.59488894, 0.23484048); + 0.23484048, 0); return t; } diff --git a/src/onset/peakpicker.h b/src/onset/peakpicker.h index c137cc86..dafaf915 100644 --- a/src/onset/peakpicker.h +++ b/src/onset/peakpicker.h @@ -26,8 +26,8 @@ */ -#ifndef _AUBIO_PEAKPICK_H -#define _AUBIO_PEAKPICK_H +#ifndef AUBIO_PEAKPICK_H +#define AUBIO_PEAKPICK_H #ifdef __cplusplus extern "C" { @@ -54,4 +54,4 @@ smpl_t aubio_peakpicker_get_threshold(aubio_peakpicker_t * p); } #endif -#endif /* _AUBIO_PEAKPICK_H */ +#endif /* AUBIO_PEAKPICK_H */ diff --git a/src/pitch/pitch.c b/src/pitch/pitch.c index 29dd758e..9febb365 100644 --- a/src/pitch/pitch.c +++ b/src/pitch/pitch.c @@ -61,7 +61,7 @@ typedef enum } aubio_pitch_mode; /** callback to get pitch candidate, defined below */ -typedef void (*aubio_pitch_detect_t) (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf); +typedef void (*aubio_pitch_detect_t) (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf); /** callback to convert pitch from one unit to another, defined below */ typedef smpl_t(*aubio_pitch_convert_t) (smpl_t value, uint_t samplerate, uint_t bufsize); @@ -78,6 +78,7 @@ struct _aubio_pitch_t uint_t bufsize; /**< buffer size */ void *p_object; /**< pointer to pitch object */ aubio_filter_t *filter; /**< filter */ + fvec_t *filtered; /**< filtered input */ aubio_pvoc_t *pv; /**< phase vocoder for mcomb */ cvec_t *fftgrain; /**< spectral frame for mcomb */ fvec_t *buf; /**< temporary buffer for yin */ @@ -88,28 +89,32 @@ struct _aubio_pitch_t }; /* callback functions for pitch detection */ -static void aubio_pitch_do_mcomb (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf); -static void aubio_pitch_do_yin (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf); -static void aubio_pitch_do_schmitt (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf); -static void aubio_pitch_do_fcomb (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf); -static void aubio_pitch_do_yinfft (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf); -static void aubio_pitch_do_specacf (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf); - -/* conversion functions for frequency conversions */ -smpl_t freqconvbin (smpl_t f, uint_t samplerate, uint_t bufsize); -smpl_t freqconvmidi (smpl_t f, uint_t samplerate, uint_t bufsize); -smpl_t freqconvpass (smpl_t f, uint_t samplerate, uint_t bufsize); +static void aubio_pitch_do_mcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf); +static void aubio_pitch_do_yin (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf); +static void aubio_pitch_do_schmitt (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf); +static void aubio_pitch_do_fcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf); +static void aubio_pitch_do_yinfft (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf); +static void aubio_pitch_do_specacf (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf); + +/* internal functions for frequency conversion */ +static smpl_t freqconvbin (smpl_t f, uint_t samplerate, uint_t bufsize); +static smpl_t freqconvmidi (smpl_t f, uint_t samplerate, uint_t bufsize); +static smpl_t freqconvpass (smpl_t f, uint_t samplerate, uint_t bufsize); /* adapter to stack ibuf new samples at the end of buf, and trim `buf` to `bufsize` */ -void aubio_pitch_slideblock (aubio_pitch_t * p, fvec_t * ibuf); +void aubio_pitch_slideblock (aubio_pitch_t * p, const fvec_t * ibuf); aubio_pitch_t * -new_aubio_pitch (char_t * pitch_mode, +new_aubio_pitch (const char_t * pitch_mode, uint_t bufsize, uint_t hopsize, uint_t samplerate) { aubio_pitch_t *p = AUBIO_NEW (aubio_pitch_t); aubio_pitch_type pitch_type; + if (pitch_mode == NULL) { + AUBIO_ERR ("pitch: can not use ‘NULL‘ for pitch detection method\n"); + goto beach; + } if (strcmp (pitch_mode, "mcomb") == 0) pitch_type = aubio_pitcht_mcomb; else if (strcmp (pitch_mode, "yinfft") == 0) @@ -125,23 +130,22 @@ new_aubio_pitch (char_t * pitch_mode, else if (strcmp (pitch_mode, "default") == 0) pitch_type = aubio_pitcht_default; else { - AUBIO_ERR ("unknown pitch detection method %s, using default.\n", - pitch_mode); - pitch_type = aubio_pitcht_default; + AUBIO_ERR ("pitch: unknown pitch detection method ‘%s’\n", pitch_mode); + goto beach; } // check parameters are valid if ((sint_t)hopsize < 1) { - AUBIO_ERR("onset: got hopsize %d, but can not be < 1\n", hopsize); + AUBIO_ERR("pitch: got hopsize %d, but can not be < 1\n", hopsize); goto beach; } else if ((sint_t)bufsize < 1) { - AUBIO_ERR("onset: got buffer_size %d, but can not be < 1\n", bufsize); + AUBIO_ERR("pitch: got buffer_size %d, but can not be < 1\n", bufsize); goto beach; } else if (bufsize < hopsize) { - AUBIO_ERR("onset: hop size (%d) is larger than win size (%d)\n", bufsize, hopsize); + AUBIO_ERR("pitch: hop size (%d) is larger than win size (%d)\n", hopsize, bufsize); goto beach; } else if ((sint_t)samplerate < 1) { - AUBIO_ERR("onset: samplerate (%d) can not be < 1\n", samplerate); + AUBIO_ERR("pitch: samplerate (%d) can not be < 1\n", samplerate); goto beach; } @@ -155,12 +159,15 @@ new_aubio_pitch (char_t * pitch_mode, case aubio_pitcht_yin: p->buf = new_fvec (bufsize); p->p_object = new_aubio_pitchyin (bufsize); + if (!p->p_object) goto beach; p->detect_cb = aubio_pitch_do_yin; p->conf_cb = (aubio_pitch_get_conf_t)aubio_pitchyin_get_confidence; aubio_pitchyin_set_tolerance (p->p_object, 0.15); break; case aubio_pitcht_mcomb: + p->filtered = new_fvec (hopsize); p->pv = new_aubio_pvoc (bufsize, hopsize); + if (!p->pv) goto beach; p->fftgrain = new_cvec (bufsize); p->p_object = new_aubio_pitchmcomb (bufsize, hopsize); p->filter = new_aubio_filter_c_weighting (samplerate); @@ -169,6 +176,7 @@ new_aubio_pitch (char_t * pitch_mode, case aubio_pitcht_fcomb: p->buf = new_fvec (bufsize); p->p_object = new_aubio_pitchfcomb (bufsize, hopsize); + if (!p->p_object) goto beach; p->detect_cb = aubio_pitch_do_fcomb; break; case aubio_pitcht_schmitt: @@ -179,6 +187,7 @@ new_aubio_pitch (char_t * pitch_mode, case aubio_pitcht_yinfft: p->buf = new_fvec (bufsize); p->p_object = new_aubio_pitchyinfft (samplerate, bufsize); + if (!p->p_object) goto beach; p->detect_cb = aubio_pitch_do_yinfft; p->conf_cb = (aubio_pitch_get_conf_t)aubio_pitchyinfft_get_confidence; aubio_pitchyinfft_set_tolerance (p->p_object, 0.85); @@ -186,6 +195,7 @@ new_aubio_pitch (char_t * pitch_mode, case aubio_pitcht_specacf: p->buf = new_fvec (bufsize); p->p_object = new_aubio_pitchspecacf (bufsize); + if (!p->p_object) goto beach; p->detect_cb = aubio_pitch_do_specacf; p->conf_cb = (aubio_pitch_get_conf_t)aubio_pitchspecacf_get_tolerance; aubio_pitchspecacf_set_tolerance (p->p_object, 0.85); @@ -196,6 +206,8 @@ new_aubio_pitch (char_t * pitch_mode, return p; beach: + if (p->filtered) del_fvec(p->filtered); + if (p->buf) del_fvec(p->buf); AUBIO_FREE(p); return NULL; } @@ -209,6 +221,7 @@ del_aubio_pitch (aubio_pitch_t * p) del_aubio_pitchyin (p->p_object); break; case aubio_pitcht_mcomb: + del_fvec (p->filtered); del_aubio_pvoc (p->pv); del_cvec (p->fftgrain); del_aubio_filter (p->filter); @@ -237,7 +250,7 @@ del_aubio_pitch (aubio_pitch_t * p) } void -aubio_pitch_slideblock (aubio_pitch_t * p, fvec_t * ibuf) +aubio_pitch_slideblock (aubio_pitch_t * p, const fvec_t * ibuf) { uint_t overlap_size = p->buf->length - ibuf->length; #if 1 //!HAVE_MEMCPY_HACKS @@ -257,7 +270,7 @@ aubio_pitch_slideblock (aubio_pitch_t * p, fvec_t * ibuf) } uint_t -aubio_pitch_set_unit (aubio_pitch_t * p, char_t * pitch_unit) +aubio_pitch_set_unit (aubio_pitch_t * p, const char_t * pitch_unit) { uint_t err = AUBIO_OK; aubio_pitch_mode pitch_mode; @@ -280,7 +293,8 @@ aubio_pitch_set_unit (aubio_pitch_t * p, char_t * pitch_unit) else if (strcmp (pitch_unit, "default") == 0) pitch_mode = aubio_pitchm_default; else { - AUBIO_ERR ("unknown pitch detection unit %s, using default\n", pitch_unit); + AUBIO_WRN("pitch: unknown pitch detection unit ‘%s’, using default\n", + pitch_unit); pitch_mode = aubio_pitchm_default; err = AUBIO_FAIL; } @@ -321,6 +335,23 @@ aubio_pitch_set_tolerance (aubio_pitch_t * p, smpl_t tol) return AUBIO_OK; } +smpl_t +aubio_pitch_get_tolerance (aubio_pitch_t * p) +{ + smpl_t tolerance = 1.; + switch (p->type) { + case aubio_pitcht_yin: + tolerance = aubio_pitchyin_get_tolerance (p->p_object); + break; + case aubio_pitcht_yinfft: + tolerance = aubio_pitchyinfft_get_tolerance (p->p_object); + break; + default: + break; + } + return tolerance; +} + uint_t aubio_pitch_set_silence (aubio_pitch_t * p, smpl_t silence) { @@ -328,7 +359,7 @@ aubio_pitch_set_silence (aubio_pitch_t * p, smpl_t silence) p->silence = silence; return AUBIO_OK; } else { - AUBIO_ERR("pitch: could not set silence to %.2f", silence); + AUBIO_WRN("pitch: could not set silence to %.2f\n", silence); return AUBIO_FAIL; } } @@ -342,7 +373,7 @@ aubio_pitch_get_silence (aubio_pitch_t * p) /* do method, calling the detection callback, then the conversion callback */ void -aubio_pitch_do (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf) +aubio_pitch_do (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf) { p->detect_cb (p, ibuf, obuf); if (aubio_silence_detection(ibuf, p->silence) == 1) { @@ -353,16 +384,16 @@ aubio_pitch_do (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf) /* do method for each algorithm */ void -aubio_pitch_do_mcomb (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf) +aubio_pitch_do_mcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf) { - aubio_filter_do (p->filter, ibuf); + aubio_filter_do_outplace (p->filter, ibuf, p->filtered); aubio_pvoc_do (p->pv, ibuf, p->fftgrain); aubio_pitchmcomb_do (p->p_object, p->fftgrain, obuf); obuf->data[0] = aubio_bintofreq (obuf->data[0], p->samplerate, p->bufsize); } void -aubio_pitch_do_yin (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf) +aubio_pitch_do_yin (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf) { smpl_t pitch = 0.; aubio_pitch_slideblock (p, ibuf); @@ -378,7 +409,7 @@ aubio_pitch_do_yin (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf) void -aubio_pitch_do_yinfft (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf) +aubio_pitch_do_yinfft (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf) { smpl_t pitch = 0.; aubio_pitch_slideblock (p, ibuf); @@ -393,7 +424,7 @@ aubio_pitch_do_yinfft (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * obuf) } void -aubio_pitch_do_specacf (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * out) +aubio_pitch_do_specacf (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * out) { smpl_t pitch = 0., period; aubio_pitch_slideblock (p, ibuf); @@ -409,7 +440,7 @@ aubio_pitch_do_specacf (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * out) } void -aubio_pitch_do_fcomb (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * out) +aubio_pitch_do_fcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * out) { aubio_pitch_slideblock (p, ibuf); aubio_pitchfcomb_do (p->p_object, p->buf, out); @@ -417,7 +448,7 @@ aubio_pitch_do_fcomb (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * out) } void -aubio_pitch_do_schmitt (aubio_pitch_t * p, fvec_t * ibuf, fvec_t * out) +aubio_pitch_do_schmitt (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * out) { smpl_t period, pitch = 0.; aubio_pitch_slideblock (p, ibuf); diff --git a/src/pitch/pitch.h b/src/pitch/pitch.h index a7780714..253ee648 100644 --- a/src/pitch/pitch.h +++ b/src/pitch/pitch.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_PITCH_H -#define _AUBIO_PITCH_H +#ifndef AUBIO_PITCH_H +#define AUBIO_PITCH_H #ifdef __cplusplus extern "C" { @@ -107,7 +107,7 @@ typedef struct _aubio_pitch_t aubio_pitch_t; \param out output pitch candidates of size [1] */ -void aubio_pitch_do (aubio_pitch_t * o, fvec_t * in, fvec_t * out); +void aubio_pitch_do (aubio_pitch_t * o, const fvec_t * in, fvec_t * out); /** change yin or yinfft tolerance threshold @@ -117,6 +117,14 @@ void aubio_pitch_do (aubio_pitch_t * o, fvec_t * in, fvec_t * out); */ uint_t aubio_pitch_set_tolerance (aubio_pitch_t * o, smpl_t tol); +/** get yin or yinfft tolerance threshold + + \param o pitch detection object as returned by new_aubio_pitch() + \return tolerance (default is 0.15 for yin and 0.85 for yinfft) + +*/ +smpl_t aubio_pitch_get_tolerance (aubio_pitch_t * o); + /** deletion of the pitch detection object \param o pitch detection object as returned by new_aubio_pitch() @@ -134,7 +142,7 @@ void del_aubio_pitch (aubio_pitch_t * o); \return newly created ::aubio_pitch_t */ -aubio_pitch_t *new_aubio_pitch (char_t * method, +aubio_pitch_t *new_aubio_pitch (const char_t * method, uint_t buf_size, uint_t hop_size, uint_t samplerate); /** set the output unit of the pitch detection object @@ -142,10 +150,12 @@ aubio_pitch_t *new_aubio_pitch (char_t * method, \param o pitch detection object as returned by new_aubio_pitch() \param mode set pitch units for output + mode can be one of "Hz", "midi", "cent", or "bin". Defaults to "Hz". + \return 0 if successfull, non-zero otherwise */ -uint_t aubio_pitch_set_unit (aubio_pitch_t * o, char_t * mode); +uint_t aubio_pitch_set_unit (aubio_pitch_t * o, const char_t * mode); /** set the silence threshold of the pitch detection object @@ -179,4 +189,4 @@ smpl_t aubio_pitch_get_confidence (aubio_pitch_t * o); } #endif -#endif /* _AUBIO_PITCH_H */ +#endif /* AUBIO_PITCH_H */ diff --git a/src/pitch/pitchfcomb.c b/src/pitch/pitchfcomb.c index 83bc6b11..5cc49b9b 100644 --- a/src/pitch/pitchfcomb.c +++ b/src/pitch/pitchfcomb.c @@ -53,17 +53,22 @@ new_aubio_pitchfcomb (uint_t bufsize, uint_t hopsize) aubio_pitchfcomb_t *p = AUBIO_NEW (aubio_pitchfcomb_t); p->fftSize = bufsize; p->stepSize = hopsize; + p->fft = new_aubio_fft (bufsize); + if (!p->fft) goto beach; p->winput = new_fvec (bufsize); p->fftOut = new_cvec (bufsize); p->fftLastPhase = new_fvec (bufsize); - p->fft = new_aubio_fft (bufsize); p->win = new_aubio_window ("hanning", bufsize); return p; + +beach: + AUBIO_FREE(p); + return NULL; } /* input must be stepsize long */ void -aubio_pitchfcomb_do (aubio_pitchfcomb_t * p, fvec_t * input, fvec_t * output) +aubio_pitchfcomb_do (aubio_pitchfcomb_t * p, const fvec_t * input, fvec_t * output) { uint_t k, l, maxharm = 0; smpl_t phaseDifference = TWO_PI * (smpl_t) p->stepSize / (smpl_t) p->fftSize; diff --git a/src/pitch/pitchfcomb.h b/src/pitch/pitchfcomb.h index 9203cc12..50167088 100644 --- a/src/pitch/pitchfcomb.h +++ b/src/pitch/pitchfcomb.h @@ -34,8 +34,8 @@ */ -#ifndef _AUBIO_PITCHFCOMB_H -#define _AUBIO_PITCHFCOMB_H +#ifndef AUBIO_PITCHFCOMB_H +#define AUBIO_PITCHFCOMB_H #ifdef __cplusplus extern "C" { @@ -51,7 +51,7 @@ typedef struct _aubio_pitchfcomb_t aubio_pitchfcomb_t; \param output pitch candidates in bins */ -void aubio_pitchfcomb_do (aubio_pitchfcomb_t * p, fvec_t * input, +void aubio_pitchfcomb_do (aubio_pitchfcomb_t * p, const fvec_t * input, fvec_t * output); /** creation of the pitch detection object @@ -73,4 +73,4 @@ void del_aubio_pitchfcomb (aubio_pitchfcomb_t * p); } #endif -#endif /* _AUBIO_PITCHFCOMB_H */ +#endif /* AUBIO_PITCHFCOMB_H */ diff --git a/src/pitch/pitchmcomb.c b/src/pitch/pitchmcomb.c index b8714210..f9ec6107 100644 --- a/src/pitch/pitchmcomb.c +++ b/src/pitch/pitchmcomb.c @@ -31,9 +31,9 @@ typedef struct _aubio_spectralcandidate_t aubio_spectralcandidate_t; uint_t aubio_pitchmcomb_get_root_peak (aubio_spectralpeak_t * peaks, uint_t length); uint_t aubio_pitchmcomb_quadpick (aubio_spectralpeak_t * spectral_peaks, - fvec_t * X); -void aubio_pitchmcomb_spectral_pp (aubio_pitchmcomb_t * p, fvec_t * oldmag); -void aubio_pitchmcomb_combdet (aubio_pitchmcomb_t * p, fvec_t * newmag); + const fvec_t * X); +void aubio_pitchmcomb_spectral_pp (aubio_pitchmcomb_t * p, const fvec_t * oldmag); +void aubio_pitchmcomb_combdet (aubio_pitchmcomb_t * p, const fvec_t * newmag); /* not used but useful : sort by amplitudes (or anything else) * sort_pitchpeak(peaks, length); */ @@ -42,7 +42,7 @@ static sint_t aubio_pitchmcomb_sort_peak_comp (const void *x, const void *y); /** sort spectral_peak against their mag */ void aubio_pitchmcomb_sort_peak (aubio_spectralpeak_t * peaks, uint_t nbins); /** select the best candidates */ -uint_t aubio_pitch_cands (aubio_pitchmcomb_t * p, cvec_t * fftgrain, +uint_t aubio_pitch_cands (aubio_pitchmcomb_t * p, const cvec_t * fftgrain, smpl_t * cands); /** sort spectral_candidate against their comb ene */ @@ -101,7 +101,7 @@ struct _aubio_spectralcandidate_t void -aubio_pitchmcomb_do (aubio_pitchmcomb_t * p, cvec_t * fftgrain, fvec_t * output) +aubio_pitchmcomb_do (aubio_pitchmcomb_t * p, const cvec_t * fftgrain, fvec_t * output) { uint_t j; smpl_t instfreq; @@ -134,7 +134,7 @@ aubio_pitchmcomb_do (aubio_pitchmcomb_t * p, cvec_t * fftgrain, fvec_t * output) } uint_t -aubio_pitch_cands (aubio_pitchmcomb_t * p, cvec_t * fftgrain, smpl_t * cands) +aubio_pitch_cands (aubio_pitchmcomb_t * p, const cvec_t * fftgrain, smpl_t * cands) { uint_t j; uint_t k; @@ -165,7 +165,7 @@ aubio_pitch_cands (aubio_pitchmcomb_t * p, cvec_t * fftgrain, smpl_t * cands) } void -aubio_pitchmcomb_spectral_pp (aubio_pitchmcomb_t * p, fvec_t * newmag) +aubio_pitchmcomb_spectral_pp (aubio_pitchmcomb_t * p, const fvec_t * newmag) { fvec_t *mag = (fvec_t *) p->scratch; fvec_t *tmp = (fvec_t *) p->scratch2; @@ -197,7 +197,7 @@ aubio_pitchmcomb_spectral_pp (aubio_pitchmcomb_t * p, fvec_t * newmag) } void -aubio_pitchmcomb_combdet (aubio_pitchmcomb_t * p, fvec_t * newmag) +aubio_pitchmcomb_combdet (aubio_pitchmcomb_t * p, const fvec_t * newmag) { aubio_spectralpeak_t *peaks = (aubio_spectralpeak_t *) p->peaks; aubio_spectralcandidate_t **candidate = @@ -285,7 +285,7 @@ aubio_pitchmcomb_combdet (aubio_pitchmcomb_t * p, fvec_t * newmag) * \bug peak-picking too picky, sometimes counts too many peaks ? */ uint_t -aubio_pitchmcomb_quadpick (aubio_spectralpeak_t * spectral_peaks, fvec_t * X) +aubio_pitchmcomb_quadpick (aubio_spectralpeak_t * spectral_peaks, const fvec_t * X) { uint_t j, ispeak, count = 0; for (j = 1; j < X->length - 1; j++) { @@ -364,7 +364,7 @@ new_aubio_pitchmcomb (uint_t bufsize, uint_t hopsize) /* bug: should check if size / 8 > post+pre+1 */ uint_t i, j; uint_t spec_size; - p->spec_partition = 4; + p->spec_partition = 2; p->ncand = 5; p->npartials = 5; p->cutoff = 1.; @@ -376,7 +376,7 @@ new_aubio_pitchmcomb (uint_t bufsize, uint_t hopsize) p->goodcandidate = 0; p->phasefreq = bufsize / hopsize / TWO_PI; p->phasediff = TWO_PI * hopsize / bufsize; - spec_size = bufsize / p->spec_partition; + spec_size = bufsize / p->spec_partition + 1; //p->pickerfn = quadpick; //p->biquad = new_biquad(0.1600,0.3200,0.1600, -0.5949, 0.2348); /* allocate temp memory */ diff --git a/src/pitch/pitchmcomb.h b/src/pitch/pitchmcomb.h index 15c6c0da..88f117f2 100644 --- a/src/pitch/pitchmcomb.h +++ b/src/pitch/pitchmcomb.h @@ -35,8 +35,8 @@ */ -#ifndef _AUBIO_PITCHMCOMB_H -#define _AUBIO_PITCHMCOMB_H +#ifndef AUBIO_PITCHMCOMB_H +#define AUBIO_PITCHMCOMB_H #ifdef __cplusplus extern "C" { @@ -52,7 +52,7 @@ typedef struct _aubio_pitchmcomb_t aubio_pitchmcomb_t; \param out_cands pitch candidate frequenciess, in bins */ -void aubio_pitchmcomb_do (aubio_pitchmcomb_t * p, cvec_t * in_fftgrain, +void aubio_pitchmcomb_do (aubio_pitchmcomb_t * p, const cvec_t * in_fftgrain, fvec_t * out_cands); /** creation of the pitch detection object @@ -74,4 +74,4 @@ void del_aubio_pitchmcomb (aubio_pitchmcomb_t * p); } #endif -#endif /* _AUBIO_PITCHMCOMB_H */ +#endif /* AUBIO_PITCHMCOMB_H */ diff --git a/src/pitch/pitchschmitt.c b/src/pitch/pitchschmitt.c index 0c9acbc7..06a34e8e 100644 --- a/src/pitch/pitchschmitt.c +++ b/src/pitch/pitchschmitt.c @@ -47,7 +47,7 @@ new_aubio_pitchschmitt (uint_t size) } void -aubio_pitchschmitt_do (aubio_pitchschmitt_t * p, fvec_t * input, +aubio_pitchschmitt_do (aubio_pitchschmitt_t * p, const fvec_t * input, fvec_t * output) { uint_t j; diff --git a/src/pitch/pitchschmitt.h b/src/pitch/pitchschmitt.h index 6052bad7..db952c34 100644 --- a/src/pitch/pitchschmitt.h +++ b/src/pitch/pitchschmitt.h @@ -34,8 +34,8 @@ */ -#ifndef _AUBIO_PITCHSCHMITT_H -#define _AUBIO_PITCHSCHMITT_H +#ifndef AUBIO_PITCHSCHMITT_H +#define AUBIO_PITCHSCHMITT_H #ifdef __cplusplus extern "C" { @@ -51,7 +51,7 @@ typedef struct _aubio_pitchschmitt_t aubio_pitchschmitt_t; \param cands_out pitch period estimates, in samples */ -void aubio_pitchschmitt_do (aubio_pitchschmitt_t * p, fvec_t * samples_in, +void aubio_pitchschmitt_do (aubio_pitchschmitt_t * p, const fvec_t * samples_in, fvec_t * cands_out); /** creation of the pitch detection object @@ -72,5 +72,4 @@ void del_aubio_pitchschmitt (aubio_pitchschmitt_t * p); } #endif -#endif /* _AUBIO_PITCHSCHMITT_H */ - +#endif /* AUBIO_PITCHSCHMITT_H */ diff --git a/src/pitch/pitchspecacf.c b/src/pitch/pitchspecacf.c index cae5eecc..051d90bc 100644 --- a/src/pitch/pitchspecacf.c +++ b/src/pitch/pitchspecacf.c @@ -42,19 +42,24 @@ aubio_pitchspecacf_t * new_aubio_pitchspecacf (uint_t bufsize) { aubio_pitchspecacf_t *p = AUBIO_NEW (aubio_pitchspecacf_t); + p->fft = new_aubio_fft (bufsize); + if (!p->fft) goto beach; p->win = new_aubio_window ("hanningz", bufsize); p->winput = new_fvec (bufsize); - p->fft = new_aubio_fft (bufsize); p->fftout = new_fvec (bufsize); p->sqrmag = new_fvec (bufsize); p->acf = new_fvec (bufsize / 2 + 1); p->tol = 1.; p->confidence = 0.; return p; + +beach: + AUBIO_FREE(p); + return NULL; } void -aubio_pitchspecacf_do (aubio_pitchspecacf_t * p, fvec_t * input, fvec_t * output) +aubio_pitchspecacf_do (aubio_pitchspecacf_t * p, const fvec_t * input, fvec_t * output) { uint_t l, tau; fvec_t *fftout = p->fftout; @@ -91,7 +96,7 @@ del_aubio_pitchspecacf (aubio_pitchspecacf_t * p) } smpl_t -aubio_pitchspecacf_get_confidence (aubio_pitchspecacf_t * o) { +aubio_pitchspecacf_get_confidence (const aubio_pitchspecacf_t * o) { // no confidence for now return o->confidence; } @@ -104,7 +109,7 @@ aubio_pitchspecacf_set_tolerance (aubio_pitchspecacf_t * p, smpl_t tol) } smpl_t -aubio_pitchspecacf_get_tolerance (aubio_pitchspecacf_t * p) +aubio_pitchspecacf_get_tolerance (const aubio_pitchspecacf_t * p) { return p->tol; } diff --git a/src/pitch/pitchspecacf.h b/src/pitch/pitchspecacf.h index 5d3d1b00..e3c9c4c1 100644 --- a/src/pitch/pitchspecacf.h +++ b/src/pitch/pitchspecacf.h @@ -38,8 +38,8 @@ */ -#ifndef _AUBIO_PITCHSPECACF_H -#define _AUBIO_PITCHSPECACF_H +#ifndef AUBIO_PITCHSPECACF_H +#define AUBIO_PITCHSPECACF_H #ifdef __cplusplus extern "C" { @@ -55,7 +55,7 @@ typedef struct _aubio_pitchspecacf_t aubio_pitchspecacf_t; \param cands_out pitch period candidates, in samples */ -void aubio_pitchspecacf_do (aubio_pitchspecacf_t * o, fvec_t * samples_in, fvec_t * cands_out); +void aubio_pitchspecacf_do (aubio_pitchspecacf_t * o, const fvec_t * samples_in, fvec_t * cands_out); /** creation of the pitch detection object \param buf_size size of the input buffer to analyse @@ -76,7 +76,7 @@ void del_aubio_pitchspecacf (aubio_pitchspecacf_t * o); \return tolerance parameter for minima selection [default 1.] */ -smpl_t aubio_pitchspecacf_get_tolerance (aubio_pitchspecacf_t * o); +smpl_t aubio_pitchspecacf_get_tolerance (const aubio_pitchspecacf_t * o); /** set tolerance parameter for `specacf` pitch detection object @@ -94,10 +94,10 @@ uint_t aubio_pitchspecacf_set_tolerance (aubio_pitchspecacf_t * o, smpl_t tol); \return confidence parameter */ -smpl_t aubio_pitchspecacf_get_confidence (aubio_pitchspecacf_t * o); +smpl_t aubio_pitchspecacf_get_confidence (const aubio_pitchspecacf_t * o); #ifdef __cplusplus } #endif -#endif /* _AUBIO_PITCHSPECACF_H */ +#endif /* AUBIO_PITCHSPECACF_H */ diff --git a/src/pitch/pitchyin.c b/src/pitch/pitchyin.c index 6061f911..2a7f43db 100644 --- a/src/pitch/pitchyin.c +++ b/src/pitch/pitchyin.c @@ -40,26 +40,26 @@ struct _aubio_pitchyin_t }; /** compute difference function - - \param input input signal + + \param input input signal \param yinbuf output buffer to store difference function (half shorter than input) */ void aubio_pitchyin_diff (fvec_t * input, fvec_t * yinbuf); -/** in place computation of the YIN cumulative normalised function - - \param yinbuf input signal (a square difference function), also used to store function +/** in place computation of the YIN cumulative normalised function + + \param yinbuf input signal (a square difference function), also used to store function */ void aubio_pitchyin_getcum (fvec_t * yinbuf); /** detect pitch in a YIN function - + \param yinbuf input buffer as computed by aubio_pitchyin_getcum */ -uint_t aubio_pitchyin_getpitch (fvec_t * yinbuf); +uint_t aubio_pitchyin_getpitch (const fvec_t * yinbuf); aubio_pitchyin_t * new_aubio_pitchyin (uint_t bufsize) @@ -111,7 +111,7 @@ aubio_pitchyin_getcum (fvec_t * yin) } uint_t -aubio_pitchyin_getpitch (fvec_t * yin) +aubio_pitchyin_getpitch (const fvec_t * yin) { uint_t tau = 1; do { @@ -127,32 +127,35 @@ aubio_pitchyin_getpitch (fvec_t * yin) return 0; } - /* all the above in one */ void -aubio_pitchyin_do (aubio_pitchyin_t * o, fvec_t * input, fvec_t * out) +aubio_pitchyin_do (aubio_pitchyin_t * o, const fvec_t * input, fvec_t * out) { - smpl_t tol = o->tol; - fvec_t *yin = o->yin; - uint_t j, tau = 0; + const smpl_t tol = o->tol; + fvec_t* yin = o->yin; + const smpl_t *input_data = input->data; + const uint_t length = yin->length; + smpl_t *yin_data = yin->data; + uint_t j, tau; sint_t period; - smpl_t tmp = 0., tmp2 = 0.; - yin->data[0] = 1.; - for (tau = 1; tau < yin->length; tau++) { - yin->data[tau] = 0.; - for (j = 0; j < yin->length; j++) { - tmp = input->data[j] - input->data[j + tau]; - yin->data[tau] += SQR (tmp); + smpl_t tmp, tmp2 = 0.; + + yin_data[0] = 1.; + for (tau = 1; tau < length; tau++) { + yin_data[tau] = 0.; + for (j = 0; j < length; j++) { + tmp = input_data[j] - input_data[j + tau]; + yin_data[tau] += SQR (tmp); } - tmp2 += yin->data[tau]; + tmp2 += yin_data[tau]; if (tmp2 != 0) { yin->data[tau] *= tau / tmp2; } else { yin->data[tau] = 1.; } period = tau - 3; - if (tau > 4 && (yin->data[period] < tol) && - (yin->data[period] < yin->data[period + 1])) { + if (tau > 4 && (yin_data[period] < tol) && + (yin_data[period] < yin_data[period + 1])) { out->data[0] = fvec_quadratic_peak_pos (yin, period); goto beach; } diff --git a/src/pitch/pitchyin.h b/src/pitch/pitchyin.h index 7835673b..fa32b8da 100644 --- a/src/pitch/pitchyin.h +++ b/src/pitch/pitchyin.h @@ -35,8 +35,8 @@ */ -#ifndef _AUBIO_PITCHYIN_H -#define _AUBIO_PITCHYIN_H +#ifndef AUBIO_PITCHYIN_H +#define AUBIO_PITCHYIN_H #ifdef __cplusplus extern "C" { @@ -66,7 +66,7 @@ void del_aubio_pitchyin (aubio_pitchyin_t * o); \param cands_out pitch period candidates, in samples */ -void aubio_pitchyin_do (aubio_pitchyin_t * o, fvec_t * samples_in, fvec_t * cands_out); +void aubio_pitchyin_do (aubio_pitchyin_t * o, const fvec_t * samples_in, fvec_t * cands_out); /** set tolerance parameter for YIN algorithm @@ -97,4 +97,4 @@ smpl_t aubio_pitchyin_get_confidence (aubio_pitchyin_t * o); } #endif -#endif /* _AUBIO_PITCHYIN_H */ +#endif /* AUBIO_PITCHYIN_H */ diff --git a/src/pitch/pitchyinfft.c b/src/pitch/pitchyinfft.c index 9084547b..9243e24f 100644 --- a/src/pitch/pitchyinfft.c +++ b/src/pitch/pitchyinfft.c @@ -62,6 +62,7 @@ new_aubio_pitchyinfft (uint_t samplerate, uint_t bufsize) aubio_pitchyinfft_t *p = AUBIO_NEW (aubio_pitchyinfft_t); p->winput = new_fvec (bufsize); p->fft = new_aubio_fft (bufsize); + if (!p->fft) goto beach; p->fftout = new_fvec (bufsize); p->sqrmag = new_fvec (bufsize); p->yinfft = new_fvec (bufsize / 2 + 1); @@ -95,10 +96,15 @@ new_aubio_pitchyinfft (uint_t samplerate, uint_t bufsize) // check for octave errors above 1300 Hz p->short_period = (uint_t)ROUND(samplerate / 1300.); return p; + +beach: + if (p->winput) del_fvec(p->winput); + AUBIO_FREE(p); + return NULL; } void -aubio_pitchyinfft_do (aubio_pitchyinfft_t * p, fvec_t * input, fvec_t * output) +aubio_pitchyinfft_do (aubio_pitchyinfft_t * p, const fvec_t * input, fvec_t * output) { uint_t tau, l; uint_t length = p->fftout->length; diff --git a/src/pitch/pitchyinfft.h b/src/pitch/pitchyinfft.h index 8f32ee91..ccd4e518 100644 --- a/src/pitch/pitchyinfft.h +++ b/src/pitch/pitchyinfft.h @@ -35,8 +35,8 @@ */ -#ifndef _AUBIO_PITCHYINFFT_H -#define _AUBIO_PITCHYINFFT_H +#ifndef AUBIO_PITCHYINFFT_H +#define AUBIO_PITCHYINFFT_H #ifdef __cplusplus extern "C" { @@ -52,7 +52,7 @@ typedef struct _aubio_pitchyinfft_t aubio_pitchyinfft_t; \param cands_out pitch period candidates, in samples */ -void aubio_pitchyinfft_do (aubio_pitchyinfft_t * o, fvec_t * samples_in, fvec_t * cands_out); +void aubio_pitchyinfft_do (aubio_pitchyinfft_t * o, const fvec_t * samples_in, fvec_t * cands_out); /** creation of the pitch detection object \param samplerate samplerate of the input signal @@ -96,4 +96,4 @@ smpl_t aubio_pitchyinfft_get_confidence (aubio_pitchyinfft_t * o); } #endif -#endif /* _AUBIO_PITCHYINFFT_H */ +#endif /* AUBIO_PITCHYINFFT_H */ diff --git a/src/spectral/fft.c b/src/spectral/fft.c index 8841ca2b..a6bd9027 100644 --- a/src/spectral/fft.c +++ b/src/spectral/fft.c @@ -64,12 +64,12 @@ typedef FFTW_TYPE fft_data_t; #ifdef HAVE_FFTW3F #if HAVE_AUBIO_DOUBLE -#warning "Using aubio in double precision with fftw3 in single precision" +#error "Using aubio in double precision with fftw3 in single precision" #endif /* HAVE_AUBIO_DOUBLE */ #define real_t float -#else /* HAVE_FFTW3F */ +#elif defined (HAVE_FFTW3) /* HAVE_FFTW3F */ #if !HAVE_AUBIO_DOUBLE -#warning "Using aubio in single precision with fftw3 in double precision" +#error "Using aubio in single precision with fftw3 in double precision" #endif /* HAVE_AUBIO_DOUBLE */ #define real_t double #endif /* HAVE_FFTW3F */ @@ -114,7 +114,7 @@ pthread_mutex_t aubio_fftw_mutex = PTHREAD_MUTEX_INITIALIZER; #else // using OOURA // let's use ooura instead -extern void rdft(int, int, smpl_t *, int *, smpl_t *); +extern void aubio_ooura_rdft(int, int, smpl_t *, int *, smpl_t *); #endif /* HAVE_ACCELERATE */ #endif /* HAVE_FFTW3 */ @@ -143,8 +143,8 @@ struct _aubio_fft_t { aubio_fft_t * new_aubio_fft (uint_t winsize) { aubio_fft_t * s = AUBIO_NEW(aubio_fft_t); - if ((sint_t)winsize < 1) { - AUBIO_ERR("fft: got winsize %d, but can not be < 1\n", winsize); + if ((sint_t)winsize < 2) { + AUBIO_ERR("fft: got winsize %d, but can not be < 2\n", winsize); goto beach; } #ifdef HAVE_FFTW3 @@ -188,8 +188,8 @@ aubio_fft_t * new_aubio_fft (uint_t winsize) { s->fftSetup = aubio_vDSP_create_fftsetup(s->log2fftsize, FFT_RADIX2); #else // using OOURA if (aubio_is_power_of_two(winsize) != 1) { - AUBIO_ERR("fft: can only create with sizes power of two," - " requested %d\n", winsize); + AUBIO_ERR("fft: can only create with sizes power of two, requested %d," + " try recompiling aubio with --enable-fftw3\n", winsize); goto beach; } s->winsize = winsize; @@ -212,9 +212,11 @@ void del_aubio_fft(aubio_fft_t * s) { /* destroy data */ del_fvec(s->compspec); #ifdef HAVE_FFTW3 // using FFTW3 + pthread_mutex_lock(&aubio_fftw_mutex); fftw_destroy_plan(s->pfw); fftw_destroy_plan(s->pbw); fftw_free(s->specdata); + pthread_mutex_unlock(&aubio_fftw_mutex); #else /* HAVE_FFTW3 */ #ifdef HAVE_ACCELERATE // using ACCELERATE AUBIO_FREE(s->spec.realp); @@ -230,17 +232,17 @@ void del_aubio_fft(aubio_fft_t * s) { AUBIO_FREE(s); } -void aubio_fft_do(aubio_fft_t * s, fvec_t * input, cvec_t * spectrum) { +void aubio_fft_do(aubio_fft_t * s, const fvec_t * input, cvec_t * spectrum) { aubio_fft_do_complex(s, input, s->compspec); aubio_fft_get_spectrum(s->compspec, spectrum); } -void aubio_fft_rdo(aubio_fft_t * s, cvec_t * spectrum, fvec_t * output) { +void aubio_fft_rdo(aubio_fft_t * s, const cvec_t * spectrum, fvec_t * output) { aubio_fft_get_realimag(spectrum, s->compspec); aubio_fft_rdo_complex(s, s->compspec, output); } -void aubio_fft_do_complex(aubio_fft_t * s, fvec_t * input, fvec_t * compspec) { +void aubio_fft_do_complex(aubio_fft_t * s, const fvec_t * input, fvec_t * compspec) { uint_t i; #ifndef HAVE_MEMCPY_HACKS for (i=0; i < s->winsize; i++) { @@ -280,7 +282,7 @@ void aubio_fft_do_complex(aubio_fft_t * s, fvec_t * input, fvec_t * compspec) { smpl_t scale = 1./2.; aubio_vDSP_vsmul(compspec->data, 1, &scale, compspec->data, 1, s->fft_size); #else // using OOURA - rdft(s->winsize, 1, s->in, s->ip, s->w); + aubio_ooura_rdft(s->winsize, 1, s->in, s->ip, s->w); compspec->data[0] = s->in[0]; compspec->data[s->winsize / 2] = s->in[1]; for (i = 1; i < s->fft_size - 1; i++) { @@ -291,7 +293,7 @@ void aubio_fft_do_complex(aubio_fft_t * s, fvec_t * input, fvec_t * compspec) { #endif /* HAVE_FFTW3 */ } -void aubio_fft_rdo_complex(aubio_fft_t * s, fvec_t * compspec, fvec_t * output) { +void aubio_fft_rdo_complex(aubio_fft_t * s, const fvec_t * compspec, fvec_t * output) { uint_t i; #ifdef HAVE_FFTW3 const smpl_t renorm = 1./(smpl_t)s->winsize; @@ -338,7 +340,7 @@ void aubio_fft_rdo_complex(aubio_fft_t * s, fvec_t * compspec, fvec_t * output) s->out[2 * i] = compspec->data[i]; s->out[2 * i + 1] = - compspec->data[s->winsize - i]; } - rdft(s->winsize, -1, s->out, s->ip, s->w); + aubio_ooura_rdft(s->winsize, -1, s->out, s->ip, s->w); for (i=0; i < s->winsize; i++) { output->data[i] = s->out[i] * scale; } @@ -346,17 +348,17 @@ void aubio_fft_rdo_complex(aubio_fft_t * s, fvec_t * compspec, fvec_t * output) #endif /* HAVE_FFTW3 */ } -void aubio_fft_get_spectrum(fvec_t * compspec, cvec_t * spectrum) { +void aubio_fft_get_spectrum(const fvec_t * compspec, cvec_t * spectrum) { aubio_fft_get_phas(compspec, spectrum); aubio_fft_get_norm(compspec, spectrum); } -void aubio_fft_get_realimag(cvec_t * spectrum, fvec_t * compspec) { +void aubio_fft_get_realimag(const cvec_t * spectrum, fvec_t * compspec) { aubio_fft_get_imag(spectrum, compspec); aubio_fft_get_real(spectrum, compspec); } -void aubio_fft_get_phas(fvec_t * compspec, cvec_t * spectrum) { +void aubio_fft_get_phas(const fvec_t * compspec, cvec_t * spectrum) { uint_t i; if (compspec->data[0] < 0) { spectrum->phas[0] = PI; @@ -374,7 +376,7 @@ void aubio_fft_get_phas(fvec_t * compspec, cvec_t * spectrum) { } } -void aubio_fft_get_norm(fvec_t * compspec, cvec_t * spectrum) { +void aubio_fft_get_norm(const fvec_t * compspec, cvec_t * spectrum) { uint_t i = 0; spectrum->norm[0] = ABS(compspec->data[0]); for (i=1; i < spectrum->length - 1; i++) { @@ -385,7 +387,7 @@ void aubio_fft_get_norm(fvec_t * compspec, cvec_t * spectrum) { ABS(compspec->data[compspec->length/2]); } -void aubio_fft_get_imag(cvec_t * spectrum, fvec_t * compspec) { +void aubio_fft_get_imag(const cvec_t * spectrum, fvec_t * compspec) { uint_t i; for (i = 1; i < ( compspec->length + 1 ) / 2 /*- 1 + 1*/; i++) { compspec->data[compspec->length - i] = @@ -393,7 +395,7 @@ void aubio_fft_get_imag(cvec_t * spectrum, fvec_t * compspec) { } } -void aubio_fft_get_real(cvec_t * spectrum, fvec_t * compspec) { +void aubio_fft_get_real(const cvec_t * spectrum, fvec_t * compspec) { uint_t i; for (i = 0; i < compspec->length / 2 + 1; i++) { compspec->data[i] = diff --git a/src/spectral/fft.h b/src/spectral/fft.h index 98b984e6..21072c8b 100644 --- a/src/spectral/fft.h +++ b/src/spectral/fft.h @@ -27,12 +27,12 @@ - [FFTW3](http://www.fftw.org) - [vDSP](https://developer.apple.com/library/mac/#documentation/Accelerate/Reference/vDSPRef/Reference/reference.html) - \example src/spectral/test-fft.c + \example spectral/test-fft.c */ -#ifndef _AUBIO_FFT_H -#define _AUBIO_FFT_H +#ifndef AUBIO_FFT_H +#define AUBIO_FFT_H #ifdef __cplusplus extern "C" { @@ -65,7 +65,7 @@ void del_aubio_fft(aubio_fft_t * s); \param spectrum output spectrum */ -void aubio_fft_do (aubio_fft_t *s, fvec_t * input, cvec_t * spectrum); +void aubio_fft_do (aubio_fft_t *s, const fvec_t * input, cvec_t * spectrum); /** compute backward (inverse) FFT \param s fft object as returned by new_aubio_fft @@ -73,7 +73,7 @@ void aubio_fft_do (aubio_fft_t *s, fvec_t * input, cvec_t * spectrum); \param output output signal */ -void aubio_fft_rdo (aubio_fft_t *s, cvec_t * spectrum, fvec_t * output); +void aubio_fft_rdo (aubio_fft_t *s, const cvec_t * spectrum, fvec_t * output); /** compute forward FFT @@ -82,7 +82,7 @@ void aubio_fft_rdo (aubio_fft_t *s, cvec_t * spectrum, fvec_t * output); \param compspec complex output fft real/imag */ -void aubio_fft_do_complex (aubio_fft_t *s, fvec_t * input, fvec_t * compspec); +void aubio_fft_do_complex (aubio_fft_t *s, const fvec_t * input, fvec_t * compspec); /** compute backward (inverse) FFT from real/imag \param s fft object as returned by new_aubio_fft @@ -90,7 +90,7 @@ void aubio_fft_do_complex (aubio_fft_t *s, fvec_t * input, fvec_t * compspec); \param output real output array */ -void aubio_fft_rdo_complex (aubio_fft_t *s, fvec_t * compspec, fvec_t * output); +void aubio_fft_rdo_complex (aubio_fft_t *s, const fvec_t * compspec, fvec_t * output); /** convert real/imag spectrum to norm/phas spectrum @@ -98,14 +98,14 @@ void aubio_fft_rdo_complex (aubio_fft_t *s, fvec_t * compspec, fvec_t * output); \param spectrum cvec norm/phas output array */ -void aubio_fft_get_spectrum(fvec_t * compspec, cvec_t * spectrum); +void aubio_fft_get_spectrum(const fvec_t * compspec, cvec_t * spectrum); /** convert real/imag spectrum to norm/phas spectrum \param compspec real/imag input fft array \param spectrum cvec norm/phas output array */ -void aubio_fft_get_realimag(cvec_t * spectrum, fvec_t * compspec); +void aubio_fft_get_realimag(const cvec_t * spectrum, fvec_t * compspec); /** compute phas spectrum from real/imag parts @@ -113,14 +113,14 @@ void aubio_fft_get_realimag(cvec_t * spectrum, fvec_t * compspec); \param spectrum cvec norm/phas output array */ -void aubio_fft_get_phas(fvec_t * compspec, cvec_t * spectrum); +void aubio_fft_get_phas(const fvec_t * compspec, cvec_t * spectrum); /** compute imaginary part from the norm/phas cvec \param spectrum norm/phas input array \param compspec real/imag output fft array */ -void aubio_fft_get_imag(cvec_t * spectrum, fvec_t * compspec); +void aubio_fft_get_imag(const cvec_t * spectrum, fvec_t * compspec); /** compute norm component from real/imag parts @@ -128,17 +128,17 @@ void aubio_fft_get_imag(cvec_t * spectrum, fvec_t * compspec); \param spectrum cvec norm/phas output array */ -void aubio_fft_get_norm(fvec_t * compspec, cvec_t * spectrum); +void aubio_fft_get_norm(const fvec_t * compspec, cvec_t * spectrum); /** compute real part from norm/phas components \param spectrum norm/phas input array \param compspec real/imag output fft array */ -void aubio_fft_get_real(cvec_t * spectrum, fvec_t * compspec); +void aubio_fft_get_real(const cvec_t * spectrum, fvec_t * compspec); #ifdef __cplusplus } #endif -#endif /* _AUBIO_FFT_H */ +#endif /* AUBIO_FFT_H */ diff --git a/src/spectral/filterbank.c b/src/spectral/filterbank.c index f8678e5d..0323700c 100644 --- a/src/spectral/filterbank.c +++ b/src/spectral/filterbank.c @@ -56,36 +56,30 @@ del_aubio_filterbank (aubio_filterbank_t * fb) } void -aubio_filterbank_do (aubio_filterbank_t * f, cvec_t * in, fvec_t * out) +aubio_filterbank_do (aubio_filterbank_t * f, const cvec_t * in, fvec_t * out) { - uint_t j, fn; - /* apply filter to all input channel, provided out has enough channels */ - uint_t max_filters = MIN (f->n_filters, out->length); - uint_t max_length = MIN (in->length, f->filters->length); + //uint_t max_filters = MIN (f->n_filters, out->length); + //uint_t max_length = MIN (in->length, f->filters->length); - /* reset all values in output vector */ - fvec_zeros (out); + // view cvec->norm as fvec->data + fvec_t tmp; + tmp.length = in->length; + tmp.data = in->norm; - /* for each filter */ - for (fn = 0; fn < max_filters; fn++) { - /* for each sample */ - for (j = 0; j < max_length; j++) { - out->data[fn] += in->norm[j] * f->filters->data[fn][j]; - } - } + fmat_vecmul(f->filters, &tmp, out); return; } fmat_t * -aubio_filterbank_get_coeffs (aubio_filterbank_t * f) +aubio_filterbank_get_coeffs (const aubio_filterbank_t * f) { return f->filters; } uint_t -aubio_filterbank_set_coeffs (aubio_filterbank_t * f, fmat_t * filter_coeffs) +aubio_filterbank_set_coeffs (aubio_filterbank_t * f, const fmat_t * filter_coeffs) { fmat_copy(filter_coeffs, f->filters); return 0; diff --git a/src/spectral/filterbank.h b/src/spectral/filterbank.h index 80c6982f..769b5e7a 100644 --- a/src/spectral/filterbank.h +++ b/src/spectral/filterbank.h @@ -29,8 +29,8 @@ */ -#ifndef _AUBIO_FILTERBANK_H -#define _AUBIO_FILTERBANK_H +#ifndef AUBIO_FILTERBANK_H +#define AUBIO_FILTERBANK_H #ifdef __cplusplus extern "C" @@ -66,14 +66,14 @@ void del_aubio_filterbank (aubio_filterbank_t * f); \param out output vector containing the energy found in each band, `nfilt` output values */ -void aubio_filterbank_do (aubio_filterbank_t * f, cvec_t * in, fvec_t * out); +void aubio_filterbank_do (aubio_filterbank_t * f, const cvec_t * in, fvec_t * out); /** return a pointer to the matrix object containing all filter coefficients \param f filterbank object, as returned by new_aubio_filterbank() */ -fmat_t *aubio_filterbank_get_coeffs (aubio_filterbank_t * f); +fmat_t *aubio_filterbank_get_coeffs (const aubio_filterbank_t * f); /** copy filter coefficients to the filterbank @@ -81,10 +81,10 @@ fmat_t *aubio_filterbank_get_coeffs (aubio_filterbank_t * f); \param filters filter bank coefficients to copy from */ -uint_t aubio_filterbank_set_coeffs (aubio_filterbank_t * f, fmat_t * filters); +uint_t aubio_filterbank_set_coeffs (aubio_filterbank_t * f, const fmat_t * filters); #ifdef __cplusplus } #endif -#endif /* _AUBIO_FILTERBANK_H */ +#endif /* AUBIO_FILTERBANK_H */ diff --git a/src/spectral/filterbank_mel.c b/src/spectral/filterbank_mel.c index 2fc7085e..f0595405 100644 --- a/src/spectral/filterbank_mel.c +++ b/src/spectral/filterbank_mel.c @@ -29,7 +29,7 @@ uint_t aubio_filterbank_set_triangle_bands (aubio_filterbank_t * fb, - fvec_t * freqs, smpl_t samplerate) + const fvec_t * freqs, smpl_t samplerate) { fmat_t *filters = aubio_filterbank_get_coeffs (fb); diff --git a/src/spectral/filterbank_mel.h b/src/spectral/filterbank_mel.h index 8fbb523e..77f0be05 100644 --- a/src/spectral/filterbank_mel.h +++ b/src/spectral/filterbank_mel.h @@ -31,8 +31,8 @@ */ -#ifndef _AUBIO_FILTERBANK_MEL_H -#define _AUBIO_FILTERBANK_MEL_H +#ifndef AUBIO_FILTERBANK_MEL_H +#define AUBIO_FILTERBANK_MEL_H #ifdef __cplusplus extern "C" @@ -50,7 +50,7 @@ extern "C" */ uint_t aubio_filterbank_set_triangle_bands (aubio_filterbank_t * fb, - fvec_t * freqs, smpl_t samplerate); + const fvec_t * freqs, smpl_t samplerate); /** filterbank initialization for Mel filters using Slaney's coefficients @@ -69,4 +69,4 @@ uint_t aubio_filterbank_set_mel_coeffs_slaney (aubio_filterbank_t * fb, } #endif -#endif /* _AUBIO_FILTERBANK_MEL_H */ +#endif /* AUBIO_FILTERBANK_MEL_H */ diff --git a/src/spectral/mfcc.c b/src/spectral/mfcc.c index a0e35bed..101deb82 100644 --- a/src/spectral/mfcc.c +++ b/src/spectral/mfcc.c @@ -67,21 +67,21 @@ new_aubio_mfcc (uint_t win_s, uint_t n_filters, uint_t n_coefs, /* allocating buffers */ mfcc->in_dct = new_fvec (n_filters); - mfcc->dct_coeffs = new_fmat (n_filters, n_coefs); + mfcc->dct_coeffs = new_fmat (n_coefs, n_filters); - /* compute DCT transform dct_coeffs[i][j] as + /* compute DCT transform dct_coeffs[j][i] as cos ( j * (i+.5) * PI / n_filters ) */ scaling = 1. / SQRT (n_filters / 2.); for (i = 0; i < n_filters; i++) { for (j = 0; j < n_coefs; j++) { - mfcc->dct_coeffs->data[i][j] = + mfcc->dct_coeffs->data[j][i] = scaling * COS (j * (i + 0.5) * PI / n_filters); } - mfcc->dct_coeffs->data[i][0] *= SQRT (2.) / 2.; + mfcc->dct_coeffs->data[0][i] *= SQRT (2.) / 2.; } return mfcc; -}; +} void del_aubio_mfcc (aubio_mfcc_t * mf) @@ -100,10 +100,8 @@ del_aubio_mfcc (aubio_mfcc_t * mf) void -aubio_mfcc_do (aubio_mfcc_t * mf, cvec_t * in, fvec_t * out) +aubio_mfcc_do (aubio_mfcc_t * mf, const cvec_t * in, fvec_t * out) { - uint_t j, k; - /* compute filterbank */ aubio_filterbank_do (mf->fb, in, mf->in_dct); @@ -113,16 +111,8 @@ aubio_mfcc_do (aubio_mfcc_t * mf, cvec_t * in, fvec_t * out) /* raise power */ //fvec_pow (mf->in_dct, 3.); - /* zeros output */ - fvec_zeros(out); - - /* compute discrete cosine transform */ - for (j = 0; j < mf->n_filters; j++) { - for (k = 0; k < mf->n_coefs; k++) { - out->data[k] += mf->in_dct->data[j] - * mf->dct_coeffs->data[j][k]; - } - } + /* compute mfccs */ + fmat_vecmul(mf->dct_coeffs, mf->in_dct, out); return; } diff --git a/src/spectral/mfcc.h b/src/spectral/mfcc.h index 552d141f..a170e125 100644 --- a/src/spectral/mfcc.h +++ b/src/spectral/mfcc.h @@ -34,8 +34,8 @@ */ -#ifndef _AUBIO_MFCC_H -#define _AUBIO_MFCC_H +#ifndef AUBIO_MFCC_H +#define AUBIO_MFCC_H #ifdef __cplusplus extern "C" @@ -70,10 +70,10 @@ void del_aubio_mfcc (aubio_mfcc_t * mf); \param out output mel coefficients buffer (n_coeffs long) */ -void aubio_mfcc_do (aubio_mfcc_t * mf, cvec_t * in, fvec_t * out); +void aubio_mfcc_do (aubio_mfcc_t * mf, const cvec_t * in, fvec_t * out); #ifdef __cplusplus } #endif -#endif /* _AUBIO_MFCC_H */ +#endif /* AUBIO_MFCC_H */ diff --git a/src/spectral/ooura_fft8g.c b/src/spectral/ooura_fft8g.c index 004d8de1..394bea08 100644 --- a/src/spectral/ooura_fft8g.c +++ b/src/spectral/ooura_fft8g.c @@ -2,28 +2,31 @@ // - replace all 'double' with 'smpl_t' // - include "aubio_priv.h" (for config.h and types.h) // - add missing prototypes -// - use COS and SIN macros +// - use COS, SIN, and ATAN macros +// - add cast to (smpl_t) to avoid float conversion warnings +// - declare initialization as static +// - prefix public function with aubio_ooura_ #include "aubio_priv.h" -void cdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); -void rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); -void ddct(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); -void ddst(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); -void dfct(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w); -void dfst(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w); -void makewt(int nw, int *ip, smpl_t *w); -void makect(int nc, int *ip, smpl_t *c); -void bitrv2(int n, int *ip, smpl_t *a); -void bitrv2conj(int n, int *ip, smpl_t *a); -void cftfsub(int n, smpl_t *a, smpl_t *w); -void cftbsub(int n, smpl_t *a, smpl_t *w); -void cft1st(int n, smpl_t *a, smpl_t *w); -void cftmdl(int n, int l, smpl_t *a, smpl_t *w); -void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); -void rftbsub(int n, smpl_t *a, int nc, smpl_t *c); -void dctsub(int n, smpl_t *a, int nc, smpl_t *c); -void dstsub(int n, smpl_t *a, int nc, smpl_t *c); +void aubio_ooura_cdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_ddct(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_ddst(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_dfct(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w); +void aubio_ooura_dfst(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w); +static void makewt(int nw, int *ip, smpl_t *w); +static void makect(int nc, int *ip, smpl_t *c); +static void bitrv2(int n, int *ip, smpl_t *a); +static void bitrv2conj(int n, int *ip, smpl_t *a); +static void cftfsub(int n, smpl_t *a, smpl_t *w); +static void cftbsub(int n, smpl_t *a, smpl_t *w); +static void cft1st(int n, smpl_t *a, smpl_t *w); +static void cftmdl(int n, int l, smpl_t *a, smpl_t *w); +static void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); +static void rftbsub(int n, smpl_t *a, int nc, smpl_t *c); +static void dctsub(int n, smpl_t *a, int nc, smpl_t *c); +static void dstsub(int n, smpl_t *a, int nc, smpl_t *c); /* Fast Fourier/Cosine/Sine Transform @@ -302,7 +305,7 @@ Appendix : */ -void cdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +void aubio_ooura_cdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) { void makewt(int nw, int *ip, smpl_t *w); void bitrv2(int n, int *ip, smpl_t *a); @@ -327,7 +330,7 @@ void cdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) } -void rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +void aubio_ooura_rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) { void makewt(int nw, int *ip, smpl_t *w); void makect(int nc, int *ip, smpl_t *c); @@ -361,7 +364,7 @@ void rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) a[0] += a[1]; a[1] = xi; } else { - a[1] = 0.5 * (a[0] - a[1]); + a[1] = (smpl_t)0.5 * (a[0] - a[1]); a[0] -= a[1]; if (n > 4) { rftbsub(n, a, nc, w + nw); @@ -374,7 +377,7 @@ void rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) } -void ddct(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +void aubio_ooura_ddct(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) { void makewt(int nw, int *ip, smpl_t *w); void makect(int nc, int *ip, smpl_t *c); @@ -433,7 +436,7 @@ void ddct(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) } -void ddst(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +void aubio_ooura_ddst(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) { void makewt(int nw, int *ip, smpl_t *w); void makect(int nc, int *ip, smpl_t *c); @@ -492,7 +495,7 @@ void ddst(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) } -void dfct(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w) +void aubio_ooura_dfct(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w) { void makewt(int nw, int *ip, smpl_t *w); void makect(int nc, int *ip, smpl_t *c); @@ -588,7 +591,7 @@ void dfct(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w) } -void dfst(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w) +void aubio_ooura_dfst(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w) { void makewt(int nw, int *ip, smpl_t *w); void makect(int nc, int *ip, smpl_t *c); @@ -690,7 +693,7 @@ void makewt(int nw, int *ip, smpl_t *w) ip[1] = 1; if (nw > 2) { nwh = nw >> 1; - delta = atan(1.0) / nwh; + delta = ATAN(1.0) / nwh; w[0] = 1; w[1] = 0; w[nwh] = COS(delta * nwh); @@ -724,12 +727,12 @@ void makect(int nc, int *ip, smpl_t *c) ip[1] = nc; if (nc > 1) { nch = nc >> 1; - delta = atan(1.0) / nch; - c[0] = cos(delta * nch); - c[nch] = 0.5 * c[0]; + delta = ATAN(1.0) / nch; + c[0] = COS(delta * nch); + c[nch] = (smpl_t)0.5 * c[0]; for (j = 1; j < nch; j++) { - c[j] = 0.5 * cos(delta * j); - c[nc - j] = 0.5 * sin(delta * j); + c[j] = (smpl_t)0.5 * COS(delta * j); + c[nc - j] = (smpl_t)0.5 * SIN(delta * j); } } } @@ -1585,7 +1588,7 @@ void rftfsub(int n, smpl_t *a, int nc, smpl_t *c) for (j = 2; j < m; j += 2) { k = n - j; kk += ks; - wkr = 0.5 - c[nc - kk]; + wkr = (smpl_t)0.5 - c[nc - kk]; wki = c[kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; @@ -1611,7 +1614,7 @@ void rftbsub(int n, smpl_t *a, int nc, smpl_t *c) for (j = 2; j < m; j += 2) { k = n - j; kk += ks; - wkr = 0.5 - c[nc - kk]; + wkr = (smpl_t)0.5 - c[nc - kk]; wki = c[kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; diff --git a/src/spectral/phasevoc.c b/src/spectral/phasevoc.c index 1b55e774..bb4514e3 100644 --- a/src/spectral/phasevoc.c +++ b/src/spectral/phasevoc.c @@ -44,12 +44,12 @@ struct _aubio_pvoc_t { /** returns data and dataold slided by hop_s */ -static void aubio_pvoc_swapbuffers(aubio_pvoc_t *pv, fvec_t *new); +static void aubio_pvoc_swapbuffers(aubio_pvoc_t *pv, const fvec_t *new); /** do additive synthesis from 'old' and 'cur' */ static void aubio_pvoc_addsynth(aubio_pvoc_t *pv, fvec_t * synthnew); -void aubio_pvoc_do(aubio_pvoc_t *pv, fvec_t * datanew, cvec_t *fftgrain) { +void aubio_pvoc_do(aubio_pvoc_t *pv, const fvec_t * datanew, cvec_t *fftgrain) { /* slide */ aubio_pvoc_swapbuffers(pv, datanew); /* windowing */ @@ -64,7 +64,12 @@ void aubio_pvoc_rdo(aubio_pvoc_t *pv,cvec_t * fftgrain, fvec_t * synthnew) { /* calculate rfft */ aubio_fft_rdo(pv->fft,fftgrain,pv->synth); /* unshift */ - fvec_shift(pv->synth); + fvec_ishift(pv->synth); + /* windowing */ + // if overlap = 50%, do not apply window (identity) + if (pv->hop_s * 2 < pv->win_s) { + fvec_weight(pv->synth, pv->w); + } /* additive synthesis */ aubio_pvoc_addsynth(pv, synthnew); } @@ -79,15 +84,18 @@ aubio_pvoc_t * new_aubio_pvoc (uint_t win_s, uint_t hop_s) { if ((sint_t)hop_s < 1) { AUBIO_ERR("pvoc: got hop_size %d, but can not be < 1\n", hop_s); goto beach; - } else if ((sint_t)win_s < 1) { - AUBIO_ERR("pvoc: got buffer_size %d, but can not be < 1\n", win_s); + } else if ((sint_t)win_s < 2) { + AUBIO_ERR("pvoc: got buffer_size %d, but can not be < 2\n", win_s); goto beach; } else if (win_s < hop_s) { - AUBIO_ERR("pvoc: hop size (%d) is larger than win size (%d)\n", win_s, hop_s); + AUBIO_ERR("pvoc: hop size (%d) is larger than win size (%d)\n", hop_s, win_s); goto beach; } pv->fft = new_aubio_fft (win_s); + if (pv->fft == NULL) { + goto beach; + } /* remember old */ pv->data = new_fvec (win_s); @@ -117,7 +125,16 @@ aubio_pvoc_t * new_aubio_pvoc (uint_t win_s, uint_t hop_s) { pv->end_datasize = pv->end * sizeof(smpl_t); pv->hop_datasize = pv->hop_s * sizeof(smpl_t); - pv->scale = pv->hop_s * 2. / pv->win_s; + // for reconstruction with 75% overlap + if (win_s == hop_s * 4) { + pv->scale = 2./3.; + } else if (win_s == hop_s * 8) { + pv->scale = 1./3.; + } else if (win_s == hop_s * 2) { + pv->scale = 1.; + } else { + pv->scale = .5; + } return pv; @@ -136,13 +153,13 @@ void del_aubio_pvoc(aubio_pvoc_t *pv) { AUBIO_FREE(pv); } -static void aubio_pvoc_swapbuffers(aubio_pvoc_t *pv, fvec_t *new) +static void aubio_pvoc_swapbuffers(aubio_pvoc_t *pv, const fvec_t *new) { /* some convenience pointers */ smpl_t * data = pv->data->data; smpl_t * dataold = pv->dataold->data; smpl_t * datanew = new->data; -#if !HAVE_MEMCPY_HACKS +#ifndef HAVE_MEMCPY_HACKS uint_t i; for (i = 0; i < pv->end; i++) data[i] = dataold[i]; diff --git a/src/spectral/phasevoc.h b/src/spectral/phasevoc.h index 7d9e6afe..d1e440db 100644 --- a/src/spectral/phasevoc.h +++ b/src/spectral/phasevoc.h @@ -31,8 +31,8 @@ */ -#ifndef _AUBIO_PHASEVOC_H -#define _AUBIO_PHASEVOC_H +#ifndef AUBIO_PHASEVOC_H +#define AUBIO_PHASEVOC_H #ifdef __cplusplus extern "C" { @@ -67,7 +67,7 @@ void del_aubio_pvoc(aubio_pvoc_t *pv); \param fftgrain output spectral frame */ -void aubio_pvoc_do(aubio_pvoc_t *pv, fvec_t *in, cvec_t * fftgrain); +void aubio_pvoc_do(aubio_pvoc_t *pv, const fvec_t *in, cvec_t * fftgrain); /** compute signal from spectral frame This function takes an input spectral frame fftgrain of size @@ -99,4 +99,4 @@ uint_t aubio_pvoc_get_hop(aubio_pvoc_t* pv); } #endif -#endif /* _AUBIO_PHASEVOC_H */ +#endif /* AUBIO_PHASEVOC_H */ diff --git a/src/spectral/specdesc.c b/src/spectral/specdesc.c index 5ef10625..67b95c39 100644 --- a/src/spectral/specdesc.c +++ b/src/spectral/specdesc.c @@ -26,28 +26,28 @@ #include "mathutils.h" #include "utils/hist.h" -void aubio_specdesc_energy(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); -void aubio_specdesc_hfc(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); -void aubio_specdesc_complex(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); -void aubio_specdesc_phase(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); -void aubio_specdesc_specdiff(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); -void aubio_specdesc_kl(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); -void aubio_specdesc_mkl(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); -void aubio_specdesc_specflux(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_energy(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_hfc(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_complex(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_phase(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_specdiff(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_kl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_mkl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_specflux(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); -extern void aubio_specdesc_centroid (aubio_specdesc_t * o, cvec_t * spec, +extern void aubio_specdesc_centroid (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -extern void aubio_specdesc_spread (aubio_specdesc_t * o, cvec_t * spec, +extern void aubio_specdesc_spread (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -extern void aubio_specdesc_skewness (aubio_specdesc_t * o, cvec_t * spec, +extern void aubio_specdesc_skewness (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -extern void aubio_specdesc_kurtosis (aubio_specdesc_t * o, cvec_t * spec, +extern void aubio_specdesc_kurtosis (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -extern void aubio_specdesc_slope (aubio_specdesc_t * o, cvec_t * spec, +extern void aubio_specdesc_slope (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -extern void aubio_specdesc_decrease (aubio_specdesc_t * o, cvec_t * spec, +extern void aubio_specdesc_decrease (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -extern void aubio_specdesc_rolloff (aubio_specdesc_t * o, cvec_t * spec, +extern void aubio_specdesc_rolloff (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); /** onsetdetection types */ @@ -75,7 +75,7 @@ struct _aubio_specdesc_t { aubio_specdesc_type onset_type; /**< onset detection type */ /** Pointer to aubio_specdesc_ function */ void (*funcpointer)(aubio_specdesc_t *o, - cvec_t * fftgrain, fvec_t * onset); + const cvec_t * fftgrain, fvec_t * onset); smpl_t threshold; /**< minimum norm threshold for phase and specdiff */ fvec_t *oldmag; /**< previous norm vector */ fvec_t *dev1 ; /**< current onset detection measure vector */ @@ -87,7 +87,7 @@ struct _aubio_specdesc_t { /* Energy based onset detection function */ void aubio_specdesc_energy (aubio_specdesc_t *o UNUSED, - cvec_t * fftgrain, fvec_t * onset) { + const cvec_t * fftgrain, fvec_t * onset) { uint_t j; onset->data[0] = 0.; for (j=0;jlength;j++) { @@ -97,7 +97,7 @@ void aubio_specdesc_energy (aubio_specdesc_t *o UNUSED, /* High Frequency Content onset detection function */ void aubio_specdesc_hfc(aubio_specdesc_t *o UNUSED, - cvec_t * fftgrain, fvec_t * onset){ + const cvec_t * fftgrain, fvec_t * onset){ uint_t j; onset->data[0] = 0.; for (j=0;jlength;j++) { @@ -107,7 +107,7 @@ void aubio_specdesc_hfc(aubio_specdesc_t *o UNUSED, /* Complex Domain Method onset detection function */ -void aubio_specdesc_complex (aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset) { +void aubio_specdesc_complex (aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset) { uint_t j; uint_t nbins = fftgrain->length; onset->data[0] = 0.; @@ -131,7 +131,7 @@ void aubio_specdesc_complex (aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * on /* Phase Based Method onset detection function */ void aubio_specdesc_phase(aubio_specdesc_t *o, - cvec_t * fftgrain, fvec_t * onset){ + const cvec_t * fftgrain, fvec_t * onset){ uint_t j; uint_t nbins = fftgrain->length; onset->data[0] = 0.0; @@ -161,7 +161,7 @@ void aubio_specdesc_phase(aubio_specdesc_t *o, /* Spectral difference method onset detection function */ void aubio_specdesc_specdiff(aubio_specdesc_t *o, - cvec_t * fftgrain, fvec_t * onset){ + const cvec_t * fftgrain, fvec_t * onset){ uint_t j; uint_t nbins = fftgrain->length; onset->data[0] = 0.0; @@ -188,7 +188,7 @@ void aubio_specdesc_specdiff(aubio_specdesc_t *o, /* Kullback Liebler onset detection function * note we use ln(1+Xn/(Xn-1+0.0001)) to avoid * negative (1.+) and infinite values (+1.e-10) */ -void aubio_specdesc_kl(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset){ +void aubio_specdesc_kl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ uint_t j; onset->data[0] = 0.; for (j=0;jlength;j++) { @@ -202,7 +202,7 @@ void aubio_specdesc_kl(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset){ /* Modified Kullback Liebler onset detection function * note we use ln(1+Xn/(Xn-1+0.0001)) to avoid * negative (1.+) and infinite values (+1.e-10) */ -void aubio_specdesc_mkl(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset){ +void aubio_specdesc_mkl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ uint_t j; onset->data[0] = 0.; for (j=0;jlength;j++) { @@ -213,7 +213,7 @@ void aubio_specdesc_mkl(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset){ } /* Spectral flux */ -void aubio_specdesc_specflux(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * onset){ +void aubio_specdesc_specflux(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ uint_t j; onset->data[0] = 0.; for (j=0;jlength;j++) { @@ -225,7 +225,7 @@ void aubio_specdesc_specflux(aubio_specdesc_t *o, cvec_t * fftgrain, fvec_t * on /* Generic function pointing to the choosen one */ void -aubio_specdesc_do (aubio_specdesc_t *o, cvec_t * fftgrain, +aubio_specdesc_do (aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset) { o->funcpointer(o,fftgrain,onset); } @@ -234,7 +234,7 @@ aubio_specdesc_do (aubio_specdesc_t *o, cvec_t * fftgrain, * depending on the choosen type, allocate memory as needed */ aubio_specdesc_t * -new_aubio_specdesc (char_t * onset_mode, uint_t size){ +new_aubio_specdesc (const char_t * onset_mode, uint_t size){ aubio_specdesc_t * o = AUBIO_NEW(aubio_specdesc_t); uint_t rsize = size/2+1; aubio_specdesc_type onset_type; @@ -273,12 +273,13 @@ new_aubio_specdesc (char_t * onset_mode, uint_t size){ else if (strcmp (onset_mode, "default") == 0) onset_type = aubio_onset_default; else { - AUBIO_ERR("unknown spectral descriptor type %s, using default.\n", onset_mode); - onset_type = aubio_onset_default; + AUBIO_ERR("unknown spectral descriptor type %s\n", onset_mode); + AUBIO_FREE(o); + return NULL; } switch(onset_type) { /* for both energy and hfc, only fftgrain->norm is required */ - case aubio_onset_energy: + case aubio_onset_energy: break; case aubio_onset_hfc: break; @@ -366,7 +367,7 @@ new_aubio_specdesc (char_t * onset_mode, uint_t size){ void del_aubio_specdesc (aubio_specdesc_t *o){ switch(o->onset_type) { - case aubio_onset_energy: + case aubio_onset_energy: break; case aubio_onset_hfc: break; diff --git a/src/spectral/specdesc.h b/src/spectral/specdesc.h index 1750f0ed..2cdb87ac 100644 --- a/src/spectral/specdesc.h +++ b/src/spectral/specdesc.h @@ -145,8 +145,8 @@ */ -#ifndef _AUBIO_SPECDESC_H -#define _AUBIO_SPECDESC_H +#ifndef AUBIO_SPECDESC_H +#define AUBIO_SPECDESC_H #ifdef __cplusplus extern "C" { @@ -164,7 +164,7 @@ typedef struct _aubio_specdesc_t aubio_specdesc_t; \param desc output vector (one sample long, to send to the peak picking) */ -void aubio_specdesc_do (aubio_specdesc_t * o, cvec_t * fftgrain, +void aubio_specdesc_do (aubio_specdesc_t * o, const cvec_t * fftgrain, fvec_t * desc); /** creation of a spectral description object @@ -178,7 +178,7 @@ void aubio_specdesc_do (aubio_specdesc_t * o, cvec_t * fftgrain, - `centroid`, `spread`, `skewness`, `kurtosis`, `slope`, `decrease`, `rolloff` */ -aubio_specdesc_t *new_aubio_specdesc (char_t * method, uint_t buf_size); +aubio_specdesc_t *new_aubio_specdesc (const char_t * method, uint_t buf_size); /** deletion of a spectral descriptor @@ -191,4 +191,4 @@ void del_aubio_specdesc (aubio_specdesc_t * o); } #endif -#endif /* _AUBIO_SPECDESC_H */ +#endif /* AUBIO_SPECDESC_H */ diff --git a/src/spectral/statistics.c b/src/spectral/statistics.c index fd25f0ab..4fd66c00 100644 --- a/src/spectral/statistics.c +++ b/src/spectral/statistics.c @@ -22,29 +22,29 @@ #include "cvec.h" #include "spectral/specdesc.h" -void aubio_specdesc_centroid (aubio_specdesc_t * o, cvec_t * spec, +void aubio_specdesc_centroid (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -void aubio_specdesc_spread (aubio_specdesc_t * o, cvec_t * spec, +void aubio_specdesc_spread (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -void aubio_specdesc_skewness (aubio_specdesc_t * o, cvec_t * spec, +void aubio_specdesc_skewness (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -void aubio_specdesc_kurtosis (aubio_specdesc_t * o, cvec_t * spec, +void aubio_specdesc_kurtosis (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -void aubio_specdesc_slope (aubio_specdesc_t * o, cvec_t * spec, +void aubio_specdesc_slope (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -void aubio_specdesc_decrease (aubio_specdesc_t * o, cvec_t * spec, +void aubio_specdesc_decrease (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -void aubio_specdesc_rolloff (aubio_specdesc_t * o, cvec_t * spec, +void aubio_specdesc_rolloff (aubio_specdesc_t * o, const cvec_t * spec, fvec_t * desc); -smpl_t cvec_sum (cvec_t * s); -smpl_t cvec_mean (cvec_t * s); -smpl_t cvec_centroid (cvec_t * s); -smpl_t cvec_moment (cvec_t * s, uint_t moment); +smpl_t cvec_sum (const cvec_t * s); +smpl_t cvec_mean (const cvec_t * s); +smpl_t cvec_centroid (const cvec_t * s); +smpl_t cvec_moment (const cvec_t * s, uint_t moment); smpl_t -cvec_sum (cvec_t * s) +cvec_sum (const cvec_t * s) { uint_t j; smpl_t tmp = 0.0; @@ -55,13 +55,13 @@ cvec_sum (cvec_t * s) } smpl_t -cvec_mean (cvec_t * s) +cvec_mean (const cvec_t * s) { return cvec_sum (s) / (smpl_t) (s->length); } smpl_t -cvec_centroid (cvec_t * spec) +cvec_centroid (const cvec_t * spec) { smpl_t sum = 0., sc = 0.; uint_t j; @@ -77,7 +77,7 @@ cvec_centroid (cvec_t * spec) } smpl_t -cvec_moment (cvec_t * spec, uint_t order) +cvec_moment (const cvec_t * spec, uint_t order) { smpl_t sum = 0., centroid = 0., sc = 0.; uint_t j; @@ -94,21 +94,21 @@ cvec_moment (cvec_t * spec, uint_t order) } void -aubio_specdesc_centroid (aubio_specdesc_t * o UNUSED, cvec_t * spec, +aubio_specdesc_centroid (aubio_specdesc_t * o UNUSED, const cvec_t * spec, fvec_t * desc) { desc->data[0] = cvec_centroid (spec); } void -aubio_specdesc_spread (aubio_specdesc_t * o UNUSED, cvec_t * spec, +aubio_specdesc_spread (aubio_specdesc_t * o UNUSED, const cvec_t * spec, fvec_t * desc) { desc->data[0] = cvec_moment (spec, 2); } void -aubio_specdesc_skewness (aubio_specdesc_t * o UNUSED, cvec_t * spec, +aubio_specdesc_skewness (aubio_specdesc_t * o UNUSED, const cvec_t * spec, fvec_t * desc) { smpl_t spread; @@ -122,7 +122,7 @@ aubio_specdesc_skewness (aubio_specdesc_t * o UNUSED, cvec_t * spec, } void -aubio_specdesc_kurtosis (aubio_specdesc_t * o UNUSED, cvec_t * spec, +aubio_specdesc_kurtosis (aubio_specdesc_t * o UNUSED, const cvec_t * spec, fvec_t * desc) { smpl_t spread; @@ -136,7 +136,7 @@ aubio_specdesc_kurtosis (aubio_specdesc_t * o UNUSED, cvec_t * spec, } void -aubio_specdesc_slope (aubio_specdesc_t * o UNUSED, cvec_t * spec, +aubio_specdesc_slope (aubio_specdesc_t * o UNUSED, const cvec_t * spec, fvec_t * desc) { uint_t j; @@ -164,7 +164,7 @@ aubio_specdesc_slope (aubio_specdesc_t * o UNUSED, cvec_t * spec, } void -aubio_specdesc_decrease (aubio_specdesc_t *o UNUSED, cvec_t * spec, +aubio_specdesc_decrease (aubio_specdesc_t *o UNUSED, const cvec_t * spec, fvec_t * desc) { uint_t j; smpl_t sum; @@ -182,7 +182,7 @@ aubio_specdesc_decrease (aubio_specdesc_t *o UNUSED, cvec_t * spec, } void -aubio_specdesc_rolloff (aubio_specdesc_t *o UNUSED, cvec_t * spec, +aubio_specdesc_rolloff (aubio_specdesc_t *o UNUSED, const cvec_t * spec, fvec_t *desc) { uint_t j; smpl_t cumsum, rollsum; diff --git a/src/spectral/tss.c b/src/spectral/tss.c index 9b324b9d..8b57bf32 100644 --- a/src/spectral/tss.c +++ b/src/spectral/tss.c @@ -40,7 +40,7 @@ struct _aubio_tss_t fvec_t *dev; }; -void aubio_tss_do(aubio_tss_t *o, cvec_t * input, +void aubio_tss_do(aubio_tss_t *o, const cvec_t * input, cvec_t * trans, cvec_t * stead) { uint_t j; diff --git a/src/spectral/tss.h b/src/spectral/tss.h index 51b35346..a4f54a15 100644 --- a/src/spectral/tss.h +++ b/src/spectral/tss.h @@ -36,8 +36,8 @@ */ -#ifndef _AUBIO_TSS_H -#define _AUBIO_TSS_H +#ifndef AUBIO_TSS_H +#define AUBIO_TSS_H #ifdef __cplusplus extern "C" { @@ -69,7 +69,7 @@ void del_aubio_tss (aubio_tss_t * o); \param stead output steady state components */ -void aubio_tss_do (aubio_tss_t * o, cvec_t * input, cvec_t * trans, +void aubio_tss_do (aubio_tss_t * o, const cvec_t * input, cvec_t * trans, cvec_t * stead); /** set transient / steady state separation threshold @@ -100,4 +100,4 @@ uint_t aubio_tss_set_beta (aubio_tss_t * o, smpl_t beta); } #endif -#endif /* _AUBIO_TSS_H */ +#endif /* AUBIO_TSS_H */ diff --git a/src/synth/sampler.c b/src/synth/sampler.c index e1db75d8..050f0085 100644 --- a/src/synth/sampler.c +++ b/src/synth/sampler.c @@ -19,7 +19,6 @@ */ -#include "config.h" #include "aubio_priv.h" #include "fvec.h" #include "fmat.h" @@ -55,17 +54,21 @@ beach: return NULL; } -uint_t aubio_sampler_load( aubio_sampler_t * o, char_t * uri ) +uint_t aubio_sampler_load( aubio_sampler_t * o, const char_t * uri ) { if (o->source) del_aubio_source(o->source); - o->uri = uri; + + if (o->uri) AUBIO_FREE(o->uri); + o->uri = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX)); + strncpy(o->uri, uri, strnlen(uri, PATH_MAX)); + o->source = new_aubio_source(uri, o->samplerate, o->blocksize); if (o->source) return 0; AUBIO_ERR("sampler: failed loading %s", uri); return 1; } -void aubio_sampler_do ( aubio_sampler_t * o, fvec_t * input, fvec_t * output) +void aubio_sampler_do ( aubio_sampler_t * o, const fvec_t * input, fvec_t * output) { uint_t read = 0, i; if (o->playing) { @@ -82,7 +85,7 @@ void aubio_sampler_do ( aubio_sampler_t * o, fvec_t * input, fvec_t * output) } } -void aubio_sampler_do_multi ( aubio_sampler_t * o, fmat_t * input, fmat_t * output) +void aubio_sampler_do_multi ( aubio_sampler_t * o, const fmat_t * input, fmat_t * output) { uint_t read = 0, i, j; if (o->playing) { @@ -103,7 +106,7 @@ void aubio_sampler_do_multi ( aubio_sampler_t * o, fmat_t * input, fmat_t * outp } } -uint_t aubio_sampler_get_playing ( aubio_sampler_t * o ) +uint_t aubio_sampler_get_playing ( const aubio_sampler_t * o ) { return o->playing; } @@ -130,6 +133,7 @@ void del_aubio_sampler( aubio_sampler_t * o ) if (o->source) { del_aubio_source(o->source); } + if (o->uri) AUBIO_FREE(o->uri); del_fvec(o->source_output); del_fmat(o->source_output_multi); AUBIO_FREE(o); diff --git a/src/synth/sampler.h b/src/synth/sampler.h index b27a8df8..fb1304f3 100644 --- a/src/synth/sampler.h +++ b/src/synth/sampler.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_SAMPLER_H -#define _AUBIO_SAMPLER_H +#ifndef AUBIO_SAMPLER_H +#define AUBIO_SAMPLER_H /** \file @@ -59,7 +59,7 @@ aubio_sampler_t * new_aubio_sampler(uint_t samplerate, uint_t hop_size); \return 0 if successful, non-zero otherwise */ -uint_t aubio_sampler_load( aubio_sampler_t * o, char_t * uri ); +uint_t aubio_sampler_load( aubio_sampler_t * o, const char_t * uri ); /** process sampler function @@ -73,7 +73,7 @@ If `input` is not NULL and different from `output`, then the samples from `input are added to the output. */ -void aubio_sampler_do ( aubio_sampler_t * o, fvec_t * input, fvec_t * output); +void aubio_sampler_do ( aubio_sampler_t * o, const fvec_t * input, fvec_t * output); /** process sampler function, multiple channels @@ -87,7 +87,7 @@ If `input` is not NULL and different from `output`, then the samples from `input are added to the output. */ -void aubio_sampler_do_multi ( aubio_sampler_t * o, fmat_t * input, fmat_t * output); +void aubio_sampler_do_multi ( aubio_sampler_t * o, const fmat_t * input, fmat_t * output); /** get current playing state @@ -96,7 +96,7 @@ void aubio_sampler_do_multi ( aubio_sampler_t * o, fmat_t * input, fmat_t * outp \return 0 if not playing, 1 if playing */ -uint_t aubio_sampler_get_playing ( aubio_sampler_t * o ); +uint_t aubio_sampler_get_playing ( const aubio_sampler_t * o ); /** set current playing state @@ -137,4 +137,4 @@ void del_aubio_sampler( aubio_sampler_t * o ); } #endif -#endif /* _AUBIO_SAMPLER_H */ +#endif /* AUBIO_SAMPLER_H */ diff --git a/src/synth/wavetable.c b/src/synth/wavetable.c index e4e08953..8dacde38 100644 --- a/src/synth/wavetable.c +++ b/src/synth/wavetable.c @@ -19,7 +19,6 @@ */ -#include "config.h" #include "aubio_priv.h" #include "fvec.h" #include "fmat.h" @@ -68,7 +67,7 @@ beach: return NULL; } -static smpl_t interp_2(fvec_t *input, smpl_t pos) { +static smpl_t interp_2(const fvec_t *input, smpl_t pos) { uint_t idx = (uint_t)FLOOR(pos); smpl_t frac = pos - (smpl_t)idx; smpl_t a = input->data[idx]; @@ -76,7 +75,7 @@ static smpl_t interp_2(fvec_t *input, smpl_t pos) { return a + frac * ( b - a ); } -void aubio_wavetable_do ( aubio_wavetable_t * s, fvec_t * input, fvec_t * output) +void aubio_wavetable_do ( aubio_wavetable_t * s, const fvec_t * input, fvec_t * output) { uint_t i; if (s->playing) { @@ -107,7 +106,7 @@ void aubio_wavetable_do ( aubio_wavetable_t * s, fvec_t * input, fvec_t * output } } -void aubio_wavetable_do_multi ( aubio_wavetable_t * s, fmat_t * input, fmat_t * output) +void aubio_wavetable_do_multi ( aubio_wavetable_t * s, const fmat_t * input, fmat_t * output) { uint_t i, j; if (s->playing) { @@ -142,7 +141,7 @@ void aubio_wavetable_do_multi ( aubio_wavetable_t * s, fmat_t * input, fmat_t * } } -uint_t aubio_wavetable_get_playing ( aubio_wavetable_t * s ) +uint_t aubio_wavetable_get_playing ( const aubio_wavetable_t * s ) { return s->playing; } @@ -172,7 +171,7 @@ uint_t aubio_wavetable_set_freq ( aubio_wavetable_t * s, smpl_t freq ) return aubio_parameter_set_target_value ( s->freq, freq ); } -smpl_t aubio_wavetable_get_freq ( aubio_wavetable_t * s) { +smpl_t aubio_wavetable_get_freq ( const aubio_wavetable_t * s) { return aubio_parameter_get_current_value ( s->freq); } @@ -181,7 +180,7 @@ uint_t aubio_wavetable_set_amp ( aubio_wavetable_t * s, smpl_t amp ) return aubio_parameter_set_target_value ( s->amp, amp ); } -smpl_t aubio_wavetable_get_amp ( aubio_wavetable_t * s) { +smpl_t aubio_wavetable_get_amp ( const aubio_wavetable_t * s) { return aubio_parameter_get_current_value ( s->amp ); } diff --git a/src/synth/wavetable.h b/src/synth/wavetable.h index a719ad95..b3335754 100644 --- a/src/synth/wavetable.h +++ b/src/synth/wavetable.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_WAVETABLE_H -#define _AUBIO_WAVETABLE_H +#ifndef AUBIO_WAVETABLE_H +#define AUBIO_WAVETABLE_H /** \file @@ -59,7 +59,7 @@ aubio_wavetable_t * new_aubio_wavetable(uint_t samplerate, uint_t hop_size); \return 0 if successful, non-zero otherwise */ -uint_t aubio_wavetable_load( aubio_wavetable_t * o, char_t * uri ); +uint_t aubio_wavetable_load( aubio_wavetable_t * o, const char_t * uri ); /** process wavetable function @@ -73,7 +73,7 @@ If `input` is not NULL and different from `output`, then the samples from `input are added to the output. */ -void aubio_wavetable_do ( aubio_wavetable_t * o, fvec_t * input, fvec_t * output); +void aubio_wavetable_do ( aubio_wavetable_t * o, const fvec_t * input, fvec_t * output); /** process wavetable function, multiple channels @@ -87,7 +87,7 @@ If `input` is not NULL and different from `output`, then the samples from `input are added to the output. */ -void aubio_wavetable_do_multi ( aubio_wavetable_t * o, fmat_t * input, fmat_t * output); +void aubio_wavetable_do_multi ( aubio_wavetable_t * o, const fmat_t * input, fmat_t * output); /** get current playing state @@ -96,7 +96,7 @@ void aubio_wavetable_do_multi ( aubio_wavetable_t * o, fmat_t * input, fmat_t * \return 0 if not playing, 1 if playing */ -uint_t aubio_wavetable_get_playing ( aubio_wavetable_t * o ); +uint_t aubio_wavetable_get_playing ( const aubio_wavetable_t * o ); /** set current playing state @@ -143,7 +143,7 @@ uint_t aubio_wavetable_set_freq ( aubio_wavetable_t * o, smpl_t freq ); \return current frequency, in Hz */ -smpl_t aubio_wavetable_get_freq ( aubio_wavetable_t * o); +smpl_t aubio_wavetable_get_freq ( const aubio_wavetable_t * o); /** set wavetable amplitude @@ -162,7 +162,7 @@ uint_t aubio_wavetable_set_amp ( aubio_wavetable_t * o, smpl_t amp ); \return current amplitude */ -smpl_t aubio_wavetable_get_amp ( aubio_wavetable_t * o); +smpl_t aubio_wavetable_get_amp ( const aubio_wavetable_t * o); /** destroy aubio_wavetable_t object @@ -175,4 +175,4 @@ void del_aubio_wavetable( aubio_wavetable_t * o ); } #endif -#endif /* _AUBIO_WAVETABLE_H */ +#endif /* AUBIO_WAVETABLE_H */ diff --git a/src/tempo/beattracking.c b/src/tempo/beattracking.c index 1010e016..6031fe2c 100644 --- a/src/tempo/beattracking.c +++ b/src/tempo/beattracking.c @@ -123,7 +123,7 @@ del_aubio_beattracking (aubio_beattracking_t * p) void -aubio_beattracking_do (aubio_beattracking_t * bt, fvec_t * dfframe, +aubio_beattracking_do (aubio_beattracking_t * bt, const fvec_t * dfframe, fvec_t * output) { @@ -409,19 +409,19 @@ aubio_beattracking_checkstate (aubio_beattracking_t * bt) } smpl_t -aubio_beattracking_get_period (aubio_beattracking_t * bt) +aubio_beattracking_get_period (const aubio_beattracking_t * bt) { return bt->hop_size * bt->bp; } smpl_t -aubio_beattracking_get_period_s (aubio_beattracking_t * bt) +aubio_beattracking_get_period_s (const aubio_beattracking_t * bt) { return aubio_beattracking_get_period(bt) / (smpl_t) bt->samplerate; } smpl_t -aubio_beattracking_get_bpm (aubio_beattracking_t * bt) +aubio_beattracking_get_bpm (const aubio_beattracking_t * bt) { if (bt->bp != 0) { return 60. / aubio_beattracking_get_period_s(bt); @@ -431,7 +431,7 @@ aubio_beattracking_get_bpm (aubio_beattracking_t * bt) } smpl_t -aubio_beattracking_get_confidence (aubio_beattracking_t * bt) +aubio_beattracking_get_confidence (const aubio_beattracking_t * bt) { if (bt->gp) { smpl_t acf_sum = fvec_sum(bt->acfout); diff --git a/src/tempo/beattracking.h b/src/tempo/beattracking.h index b22fbc17..bc57de86 100644 --- a/src/tempo/beattracking.h +++ b/src/tempo/beattracking.h @@ -36,8 +36,8 @@ \example tempo/test-beattracking.c */ -#ifndef _AUBIO_BEATTRACKING_H -#define _AUBIO_BEATTRACKING_H +#ifndef AUBIO_BEATTRACKING_H +#define AUBIO_BEATTRACKING_H #ifdef __cplusplus extern "C" { @@ -64,7 +64,7 @@ aubio_beattracking_t * new_aubio_beattracking(uint_t winlen, uint_t hop_size, \param out stored detected beat locations */ -void aubio_beattracking_do (aubio_beattracking_t * bt, fvec_t * dfframes, +void aubio_beattracking_do (aubio_beattracking_t * bt, const fvec_t * dfframes, fvec_t * out); /** get current beat period in samples @@ -75,7 +75,7 @@ void aubio_beattracking_do (aubio_beattracking_t * bt, fvec_t * dfframes, value is found. */ -smpl_t aubio_beattracking_get_period (aubio_beattracking_t * bt); +smpl_t aubio_beattracking_get_period (const aubio_beattracking_t * bt); /** get current beat period in seconds @@ -85,7 +85,7 @@ smpl_t aubio_beattracking_get_period (aubio_beattracking_t * bt); value is found. */ -smpl_t aubio_beattracking_get_period_s (aubio_beattracking_t * bt); +smpl_t aubio_beattracking_get_period_s (const aubio_beattracking_t * bt); /** get current tempo in bpm @@ -95,7 +95,7 @@ smpl_t aubio_beattracking_get_period_s (aubio_beattracking_t * bt); consistent value is found. */ -smpl_t aubio_beattracking_get_bpm(aubio_beattracking_t * bt); +smpl_t aubio_beattracking_get_bpm(const aubio_beattracking_t * bt); /** get current tempo confidence @@ -105,7 +105,7 @@ smpl_t aubio_beattracking_get_bpm(aubio_beattracking_t * bt); consistent value is found. */ -smpl_t aubio_beattracking_get_confidence(aubio_beattracking_t * bt); +smpl_t aubio_beattracking_get_confidence(const aubio_beattracking_t * bt); /** delete beat tracking object @@ -118,4 +118,4 @@ void del_aubio_beattracking(aubio_beattracking_t * p); } #endif -#endif /* _AUBIO_BEATTRACKING_H */ +#endif /* AUBIO_BEATTRACKING_H */ diff --git a/src/tempo/tempo.c b/src/tempo/tempo.c index 2590e6a1..80c89e99 100644 --- a/src/tempo/tempo.c +++ b/src/tempo/tempo.c @@ -28,27 +28,6 @@ #include "mathutils.h" #include "tempo/tempo.h" -// TODO implement get/set_delay - -/** set current delay - - \param o beat tracking object - - \return current delay, in samples - - */ -uint_t aubio_tempo_get_delay(aubio_tempo_t * o); - -/** set current delay - - \param o beat tracking object - \param delay delay to set tempo to, in samples - - \return `0` if successful, non-zero otherwise - - */ -uint_t aubio_tempo_set_delay(aubio_tempo_t * o, uint_t delay); - /* structure to store object state */ struct _aubio_tempo_t { aubio_specdesc_t * od; /** onset detection */ @@ -69,13 +48,13 @@ struct _aubio_tempo_t { uint_t hop_size; /** get hop_size */ uint_t total_frames; /** total frames since beginning */ uint_t last_beat; /** time of latest detected beat, in samples */ - uint_t delay; /** delay to remove to last beat, in samples */ + sint_t delay; /** delay to remove to last beat, in samples */ uint_t last_tatum; /** time of latest detected tatum, in samples */ uint_t tatum_signature; /** number of tatum between each beats */ }; /* execute tempo detection function on iput buffer */ -void aubio_tempo_do(aubio_tempo_t *o, fvec_t * input, fvec_t * tempo) +void aubio_tempo_do(aubio_tempo_t *o, const fvec_t * input, fvec_t * tempo) { uint_t i; uint_t winlen = o->winlen; @@ -100,7 +79,8 @@ void aubio_tempo_do(aubio_tempo_t *o, fvec_t * input, fvec_t * tempo) } o->blockpos++; aubio_peakpicker_do (o->pp, o->of, o->onset); - tempo->data[1] = o->onset->data[0]; + // store onset detection function in second sample of vector + //tempo->data[1] = o->onset->data[0]; thresholded = aubio_peakpicker_get_thresholded_input(o->pp); o->dfframe->data[winlen - step + o->blockpos] = thresholded->data[0]; /* end of second level loop */ @@ -124,7 +104,7 @@ void aubio_tempo_do(aubio_tempo_t *o, fvec_t * input, fvec_t * tempo) uint_t aubio_tempo_get_last (aubio_tempo_t *o) { - return o->last_beat - o->delay; + return o->last_beat + o->delay; } smpl_t aubio_tempo_get_last_s (aubio_tempo_t *o) @@ -137,15 +117,33 @@ smpl_t aubio_tempo_get_last_ms (aubio_tempo_t *o) return aubio_tempo_get_last_s (o) * 1000.; } -uint_t aubio_tempo_set_delay(aubio_tempo_t * o, uint_t delay) { +uint_t aubio_tempo_set_delay(aubio_tempo_t * o, sint_t delay) { o->delay = delay; return AUBIO_OK; } +uint_t aubio_tempo_set_delay_s(aubio_tempo_t * o, smpl_t delay) { + o->delay = delay * o->samplerate; + return AUBIO_OK; +} + +uint_t aubio_tempo_set_delay_ms(aubio_tempo_t * o, smpl_t delay) { + o->delay = 1000. * delay * o->samplerate; + return AUBIO_OK; +} + uint_t aubio_tempo_get_delay(aubio_tempo_t * o) { return o->delay; } +smpl_t aubio_tempo_get_delay_s(aubio_tempo_t * o) { + return o->delay / (smpl_t)(o->samplerate); +} + +smpl_t aubio_tempo_get_delay_ms(aubio_tempo_t * o) { + return o->delay / (smpl_t)(o->samplerate) / 1000.; +} + uint_t aubio_tempo_set_silence(aubio_tempo_t * o, smpl_t silence) { o->silence = silence; return AUBIO_OK; @@ -166,7 +164,7 @@ smpl_t aubio_tempo_get_threshold(aubio_tempo_t * o) { } /* Allocate memory for an tempo detection */ -aubio_tempo_t * new_aubio_tempo (char_t * tempo_mode, +aubio_tempo_t * new_aubio_tempo (const char_t * tempo_mode, uint_t buf_size, uint_t hop_size, uint_t samplerate) { aubio_tempo_t * o = AUBIO_NEW(aubio_tempo_t); @@ -176,8 +174,8 @@ aubio_tempo_t * new_aubio_tempo (char_t * tempo_mode, if ((sint_t)hop_size < 1) { AUBIO_ERR("tempo: got hop size %d, but can not be < 1\n", hop_size); goto beach; - } else if ((sint_t)buf_size < 1) { - AUBIO_ERR("tempo: got window size %d, but can not be < 1\n", buf_size); + } else if ((sint_t)buf_size < 2) { + AUBIO_ERR("tempo: got window size %d, but can not be < 2\n", buf_size); goto beach; } else if (buf_size < hop_size) { AUBIO_ERR("tempo: hop size (%d) is larger than window size (%d)\n", buf_size, hop_size); diff --git a/src/tempo/tempo.h b/src/tempo/tempo.h index d1f1822e..e2afe99c 100644 --- a/src/tempo/tempo.h +++ b/src/tempo/tempo.h @@ -30,8 +30,8 @@ */ -#ifndef _AUBIO_TEMPO_H -#define _AUBIO_TEMPO_H +#ifndef AUBIO_TEMPO_H +#define AUBIO_TEMPO_H #ifdef __cplusplus extern "C" { @@ -50,7 +50,7 @@ typedef struct _aubio_tempo_t aubio_tempo_t; \return newly created ::aubio_tempo_t if successful, `NULL` otherwise */ -aubio_tempo_t * new_aubio_tempo (char_t * method, +aubio_tempo_t * new_aubio_tempo (const char_t * method, uint_t buf_size, uint_t hop_size, uint_t samplerate); /** execute tempo detection @@ -60,7 +60,7 @@ aubio_tempo_t * new_aubio_tempo (char_t * method, \param tempo output beats */ -void aubio_tempo_do (aubio_tempo_t *o, fvec_t * input, fvec_t * tempo); +void aubio_tempo_do (aubio_tempo_t *o, const fvec_t * input, fvec_t * tempo); /** get the time of the latest beat detected, in samples @@ -154,13 +154,13 @@ smpl_t aubio_tempo_get_bpm(aubio_tempo_t * o); \param o beat tracking object - \return confidence with which the tempo has been observed, `0` if no - consistent value is found. + \return confidence with which the tempo has been observed, the higher the + more confidence, `0` if no consistent value is found. */ smpl_t aubio_tempo_get_confidence(aubio_tempo_t * o); -/* set number of tatum per beat +/** set number of tatum per beat \param o beat tracking object \param signature number of tatum per beat (between 1 and 64) @@ -168,7 +168,7 @@ smpl_t aubio_tempo_get_confidence(aubio_tempo_t * o); */ uint_t aubio_tempo_set_tatum_signature(aubio_tempo_t *o, uint_t signature); -/* check whether a tatum was detected in the current frame +/** check whether a tatum was detected in the current frame \param o beat tracking object @@ -177,13 +177,70 @@ uint_t aubio_tempo_set_tatum_signature(aubio_tempo_t *o, uint_t signature); */ uint_t aubio_tempo_was_tatum(aubio_tempo_t *o); -/* get position of last_tatum, in samples +/** get position of last_tatum, in samples \param o beat tracking object */ smpl_t aubio_tempo_get_last_tatum(aubio_tempo_t *o); +/** get current delay + + \param o beat tracking object + + \return current delay, in samples + + */ +uint_t aubio_tempo_get_delay(aubio_tempo_t * o); + +/** get current delay in seconds + + \param o beat tracking object + + \return current delay, in seconds + + */ +smpl_t aubio_tempo_get_delay_s(aubio_tempo_t * o); + +/** get current delay in ms + + \param o beat tracking object + + \return current delay, in milliseconds + + */ +smpl_t aubio_tempo_get_delay_ms(aubio_tempo_t * o); + +/** set current delay + + \param o beat tracking object + \param delay delay to set tempo to, in samples + + \return `0` if successful, non-zero otherwise + + */ +uint_t aubio_tempo_set_delay(aubio_tempo_t * o, sint_t delay); + +/** set current delay in seconds + + \param o beat tracking object + \param delay delay to set tempo to, in seconds + + \return `0` if successful, non-zero otherwise + + */ +uint_t aubio_tempo_set_delay_s(aubio_tempo_t * o, smpl_t delay); + +/** set current delay + + \param o beat tracking object + \param delay delay to set tempo to, in samples + + \return `0` if successful, non-zero otherwise + + */ +uint_t aubio_tempo_set_delay_ms(aubio_tempo_t * o, smpl_t delay); + /** delete tempo detection object \param o beat tracking object @@ -195,4 +252,4 @@ void del_aubio_tempo(aubio_tempo_t * o); } #endif -#endif /* _AUBIO_TEMPO_H */ +#endif /* AUBIO_TEMPO_H */ diff --git a/src/temporal/a_weighting.c b/src/temporal/a_weighting.c index a2d84303..f19f566f 100644 --- a/src/temporal/a_weighting.c +++ b/src/temporal/a_weighting.c @@ -29,17 +29,27 @@ uint_t aubio_filter_set_a_weighting (aubio_filter_t * f, uint_t samplerate) { uint_t order; lsmp_t *a, *b; lvec_t *as, *bs; - aubio_filter_set_samplerate (f, samplerate); - bs = aubio_filter_get_feedforward (f); - as = aubio_filter_get_feedback (f); - b = bs->data, a = as->data; - order = aubio_filter_get_order (f); + if ((sint_t)samplerate <= 0) { + AUBIO_ERROR("aubio_filter: failed setting A-weighting with samplerate %d\n", samplerate); + return AUBIO_FAIL; + } + if (f == NULL) { + AUBIO_ERROR("aubio_filter: failed setting A-weighting with filter NULL\n"); + return AUBIO_FAIL; + } + + order = aubio_filter_get_order (f); if (order != 7) { - AUBIO_ERROR ("order of A-weighting filter must be 7, not %d\n", order); + AUBIO_ERROR ("aubio_filter: order of A-weighting filter must be 7, not %d\n", order); return 1; } + aubio_filter_set_samplerate (f, samplerate); + bs = aubio_filter_get_feedforward (f); + as = aubio_filter_get_feedback (f); + b = bs->data, a = as->data; + /* select coefficients according to sampling frequency */ switch (samplerate) { @@ -244,6 +254,9 @@ aubio_filter_t * new_aubio_filter_a_weighting (uint_t samplerate) { aubio_filter_t *f = new_aubio_filter (7); - aubio_filter_set_a_weighting (f, samplerate); + if (aubio_filter_set_a_weighting(f,samplerate) != AUBIO_OK) { + del_aubio_filter(f); + return NULL; + } return f; } diff --git a/src/temporal/a_weighting.h b/src/temporal/a_weighting.h index 72f02b8d..49dd4d5c 100644 --- a/src/temporal/a_weighting.h +++ b/src/temporal/a_weighting.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_FILTER_A_DESIGN_H -#define _AUBIO_FILTER_A_DESIGN_H +#ifndef AUBIO_FILTER_A_DESIGN_H +#define AUBIO_FILTER_A_DESIGN_H /** \file @@ -85,4 +85,4 @@ uint_t aubio_filter_set_a_weighting (aubio_filter_t * f, uint_t samplerate); } #endif -#endif /* _AUBIO_FILTER_A_DESIGN_H */ +#endif /* AUBIO_FILTER_A_DESIGN_H */ diff --git a/src/temporal/biquad.c b/src/temporal/biquad.c index 6a03aa68..426b64fb 100644 --- a/src/temporal/biquad.c +++ b/src/temporal/biquad.c @@ -41,7 +41,7 @@ aubio_filter_set_biquad (aubio_filter_t * f, lsmp_t b0, lsmp_t b1, lsmp_t b2, bs->data[2] = b2; as->data[0] = 1.; as->data[1] = a1; - as->data[1] = a2; + as->data[2] = a2; return AUBIO_OK; } diff --git a/src/temporal/biquad.h b/src/temporal/biquad.h index 1b5147a4..1d19d942 100644 --- a/src/temporal/biquad.h +++ b/src/temporal/biquad.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_FILTER_BIQUAD_H -#define _AUBIO_FILTER_BIQUAD_H +#ifndef AUBIO_FILTER_BIQUAD_H +#define AUBIO_FILTER_BIQUAD_H /** \file @@ -72,4 +72,4 @@ aubio_filter_t *new_aubio_filter_biquad (lsmp_t b0, lsmp_t b1, lsmp_t b2, } #endif -#endif /* _AUBIO_FILTER_BIQUAD_H */ +#endif /* AUBIO_FILTER_BIQUAD_H */ diff --git a/src/temporal/c_weighting.c b/src/temporal/c_weighting.c index be658bfb..91ada717 100644 --- a/src/temporal/c_weighting.c +++ b/src/temporal/c_weighting.c @@ -29,17 +29,27 @@ uint_t aubio_filter_set_c_weighting (aubio_filter_t * f, uint_t samplerate) { uint_t order; lsmp_t *a, *b; lvec_t *as, *bs; - aubio_filter_set_samplerate (f, samplerate); - bs = aubio_filter_get_feedforward (f); - as = aubio_filter_get_feedback (f); - b = bs->data, a = as->data; - order = aubio_filter_get_order (f); + if ((sint_t)samplerate <= 0) { + AUBIO_ERROR("aubio_filter: failed setting C-weighting with samplerate %d\n", samplerate); + return AUBIO_FAIL; + } + if (f == NULL) { + AUBIO_ERROR("aubio_filter: failed setting C-weighting with filter NULL\n"); + return AUBIO_FAIL; + } + + order = aubio_filter_get_order (f); if ( order != 5 ) { - AUBIO_ERROR ("order of C-weighting filter must be 5, not %d\n", order); + AUBIO_ERROR ("aubio_filter: order of C-weighting filter must be 5, not %d\n", order); return 1; } + aubio_filter_set_samplerate (f, samplerate); + bs = aubio_filter_get_feedforward (f); + as = aubio_filter_get_feedback (f); + b = bs->data, a = as->data; + /* select coefficients according to sampling frequency */ switch (samplerate) { @@ -199,7 +209,9 @@ aubio_filter_set_c_weighting (aubio_filter_t * f, uint_t samplerate) aubio_filter_t * new_aubio_filter_c_weighting (uint_t samplerate) { aubio_filter_t * f = new_aubio_filter(5); - aubio_filter_set_c_weighting (f, samplerate); + if (aubio_filter_set_c_weighting(f,samplerate) != AUBIO_OK) { + del_aubio_filter(f); + return NULL; + } return f; } - diff --git a/src/temporal/c_weighting.h b/src/temporal/c_weighting.h index 5ef3874e..d87f61fa 100644 --- a/src/temporal/c_weighting.h +++ b/src/temporal/c_weighting.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_FILTER_C_DESIGN_H -#define _AUBIO_FILTER_C_DESIGN_H +#ifndef AUBIO_FILTER_C_DESIGN_H +#define AUBIO_FILTER_C_DESIGN_H /** \file @@ -85,4 +85,4 @@ uint_t aubio_filter_set_c_weighting (aubio_filter_t * f, uint_t samplerate); } #endif -#endif /* _AUBIO_FILTER_C_DESIGN_H */ +#endif /* AUBIO_FILTER_C_DESIGN_H */ diff --git a/src/temporal/filter.c b/src/temporal/filter.c index 0975e6f4..776d2e6f 100644 --- a/src/temporal/filter.c +++ b/src/temporal/filter.c @@ -39,7 +39,7 @@ struct _aubio_filter_t }; void -aubio_filter_do_outplace (aubio_filter_t * f, fvec_t * in, fvec_t * out) +aubio_filter_do_outplace (aubio_filter_t * f, const fvec_t * in, fvec_t * out) { fvec_copy (in, out); aubio_filter_do (f, out); @@ -93,25 +93,25 @@ aubio_filter_do_filtfilt (aubio_filter_t * f, fvec_t * in, fvec_t * tmp) } lvec_t * -aubio_filter_get_feedback (aubio_filter_t * f) +aubio_filter_get_feedback (const aubio_filter_t * f) { return f->a; } lvec_t * -aubio_filter_get_feedforward (aubio_filter_t * f) +aubio_filter_get_feedforward (const aubio_filter_t * f) { return f->b; } uint_t -aubio_filter_get_order (aubio_filter_t * f) +aubio_filter_get_order (const aubio_filter_t * f) { return f->order; } uint_t -aubio_filter_get_samplerate (aubio_filter_t * f) +aubio_filter_get_samplerate (const aubio_filter_t * f) { return f->samplerate; } @@ -134,6 +134,10 @@ aubio_filter_t * new_aubio_filter (uint_t order) { aubio_filter_t *f = AUBIO_NEW (aubio_filter_t); + if ((sint_t)order < 1) { + AUBIO_FREE(f); + return NULL; + } f->x = new_lvec (order); f->y = new_lvec (order); f->a = new_lvec (order); @@ -142,7 +146,8 @@ new_aubio_filter (uint_t order) f->samplerate = 0; f->order = order; /* set default to identity */ - f->a->data[1] = 1.; + f->a->data[0] = 1.; + f->b->data[0] = 1.; return f; } diff --git a/src/temporal/filter.h b/src/temporal/filter.h index 13c7cba5..b8b678e8 100644 --- a/src/temporal/filter.h +++ b/src/temporal/filter.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_FILTER_H -#define _AUBIO_FILTER_H +#ifndef AUBIO_FILTER_H +#define AUBIO_FILTER_H /** \file @@ -86,7 +86,7 @@ void aubio_filter_do (aubio_filter_t * f, fvec_t * in); \param out output vector to store filtered input */ -void aubio_filter_do_outplace (aubio_filter_t * f, fvec_t * in, fvec_t * out); +void aubio_filter_do_outplace (aubio_filter_t * f, const fvec_t * in, fvec_t * out); /** filter input vector forward and backward @@ -104,7 +104,7 @@ void aubio_filter_do_filtfilt (aubio_filter_t * f, fvec_t * in, fvec_t * tmp); \return a pointer to the \f$ a_0 ... a_i ... a_P \f$ coefficients */ -lvec_t *aubio_filter_get_feedback (aubio_filter_t * f); +lvec_t *aubio_filter_get_feedback (const aubio_filter_t * f); /** returns a pointer to feedforward coefficients \f$ b_i \f$ @@ -113,7 +113,7 @@ lvec_t *aubio_filter_get_feedback (aubio_filter_t * f); \return a pointer to the \f$ b_0 ... b_i ... b_P \f$ coefficients */ -lvec_t *aubio_filter_get_feedforward (aubio_filter_t * f); +lvec_t *aubio_filter_get_feedforward (const aubio_filter_t * f); /** get order of the filter @@ -122,7 +122,7 @@ lvec_t *aubio_filter_get_feedforward (aubio_filter_t * f); \return the order of the filter */ -uint_t aubio_filter_get_order (aubio_filter_t * f); +uint_t aubio_filter_get_order (const aubio_filter_t * f); /** get sampling rate of the filter @@ -131,7 +131,7 @@ uint_t aubio_filter_get_order (aubio_filter_t * f); \return the sampling rate of the filter, in Hz */ -uint_t aubio_filter_get_samplerate (aubio_filter_t * f); +uint_t aubio_filter_get_samplerate (const aubio_filter_t * f); /** get sampling rate of the filter @@ -173,4 +173,4 @@ void del_aubio_filter (aubio_filter_t * f); } #endif -#endif /* _AUBIO_FILTER_H */ +#endif /* AUBIO_FILTER_H */ diff --git a/src/temporal/resampler.c b/src/temporal/resampler.c index 041703eb..adfb08fd 100644 --- a/src/temporal/resampler.c +++ b/src/temporal/resampler.c @@ -18,13 +18,15 @@ */ -#include "config.h" - #include "aubio_priv.h" #include "fvec.h" #include "temporal/resampler.h" -#if HAVE_SAMPLERATE +#ifdef HAVE_SAMPLERATE + +#if HAVE_AUBIO_DOUBLE +#error "Should not use libsamplerate with aubio in double precision" +#endif #include /* from libsamplerate */ @@ -61,7 +63,7 @@ del_aubio_resampler (aubio_resampler_t * s) } void -aubio_resampler_do (aubio_resampler_t * s, fvec_t * input, fvec_t * output) +aubio_resampler_do (aubio_resampler_t * s, const fvec_t * input, fvec_t * output) { s->proc->input_frames = input->length; s->proc->output_frames = output->length; @@ -92,7 +94,7 @@ del_aubio_resampler (aubio_resampler_t * s UNUSED) } void -aubio_resampler_do (aubio_resampler_t * s UNUSED, fvec_t * input UNUSED, fvec_t * output UNUSED) +aubio_resampler_do (aubio_resampler_t * s UNUSED, const fvec_t * input UNUSED, fvec_t * output UNUSED) { } #endif /* HAVE_SAMPLERATE */ diff --git a/src/temporal/resampler.h b/src/temporal/resampler.h index 1684f752..7528e8ac 100644 --- a/src/temporal/resampler.h +++ b/src/temporal/resampler.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_RESAMPLER_H -#define _AUBIO_RESAMPLER_H +#ifndef AUBIO_RESAMPLER_H +#define AUBIO_RESAMPLER_H /** \file @@ -55,11 +55,11 @@ void del_aubio_resampler (aubio_resampler_t * s); \param output output buffer of size N*ratio */ -void aubio_resampler_do (aubio_resampler_t * s, fvec_t * input, +void aubio_resampler_do (aubio_resampler_t * s, const fvec_t * input, fvec_t * output); #ifdef __cplusplus } #endif -#endif /* _AUBIO_RESAMPLER_H */ +#endif /* AUBIO_RESAMPLER_H */ diff --git a/src/types.h b/src/types.h index 4200ad21..57df2e0d 100644 --- a/src/types.h +++ b/src/types.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO__TYPES_H -#define _AUBIO__TYPES_H +#ifndef AUBIO_TYPES_H +#define AUBIO_TYPES_H /** \file @@ -67,4 +67,4 @@ typedef char char_t; } #endif -#endif /* _AUBIO__TYPES_H */ +#endif /* AUBIO_TYPES_H */ diff --git a/src/utils/hist.c b/src/utils/hist.c index ea1cb1f5..9b5ab10a 100644 --- a/src/utils/hist.c +++ b/src/utils/hist.c @@ -137,7 +137,7 @@ void aubio_hist_weight (aubio_hist_t *s) { } } -smpl_t aubio_hist_mean (aubio_hist_t *s) { +smpl_t aubio_hist_mean (const aubio_hist_t *s) { uint_t j; smpl_t tmp = 0.0; for (j=0; j < s->nelems; j++) diff --git a/src/utils/hist.h b/src/utils/hist.h index a6bcd75b..d39091a4 100644 --- a/src/utils/hist.h +++ b/src/utils/hist.h @@ -25,8 +25,8 @@ * Big hacks to implement an histogram */ -#ifndef _AUBIO_HIST_H -#define _AUBIO_HIST_H +#ifndef AUBIO_HIST_H +#define AUBIO_HIST_H #ifdef __cplusplus extern "C" { @@ -50,7 +50,7 @@ void aubio_hist_do(aubio_hist_t *s, fvec_t * input); /** compute the histogram ignoring null elements */ void aubio_hist_do_notnull(aubio_hist_t *s, fvec_t * input); /** compute the mean of the histogram */ -smpl_t aubio_hist_mean(aubio_hist_t *s); +smpl_t aubio_hist_mean(const aubio_hist_t *s); /** weight the histogram */ void aubio_hist_weight(aubio_hist_t *s); /** compute dynamic histogram for non-null elements */ @@ -60,4 +60,4 @@ void aubio_hist_dyn_notnull (aubio_hist_t *s, fvec_t *input); } #endif -#endif /* _AUBIO_HIST_H */ +#endif /* AUBIO_HIST_H */ diff --git a/src/utils/log.c b/src/utils/log.c new file mode 100644 index 00000000..967c2d6c --- /dev/null +++ b/src/utils/log.c @@ -0,0 +1,92 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "log.h" + +/** array of pointers to logging functions, one per level */ +static aubio_log_function_t aubio_log_function[AUBIO_LOG_LAST_LEVEL]; +/** array of pointers to closure passed to logging functions, one per level */ +static void* aubio_log_user_data[AUBIO_LOG_LAST_LEVEL]; +/** buffer for logging messages */ +static char aubio_log_buffer[512]; + +/** private function used by default by logging functions */ +void +aubio_default_log(sint_t level, const char_t *message, void * data UNUSED) +{ + FILE *out; + out = stdout; + if (level == AUBIO_LOG_ERR || level == AUBIO_LOG_DBG || level == AUBIO_LOG_WRN) { + out = stderr; + } + fprintf(out, "%s", message); + //fflush(out); +} + +uint_t +aubio_log(sint_t level, const char_t *fmt, ...) +{ + aubio_log_function_t fun = NULL; + + va_list args; + va_start(args, fmt); + vsnprintf(aubio_log_buffer, sizeof(aubio_log_buffer), fmt, args); + va_end(args); + + if ((level >= 0) && (level < AUBIO_LOG_LAST_LEVEL)) { + fun = aubio_log_function[level]; + if (fun != NULL) { + (*fun)(level, aubio_log_buffer, aubio_log_user_data[level]); + } else { + aubio_default_log(level, aubio_log_buffer, NULL); + } + } + return AUBIO_FAIL; +} + +void +aubio_log_reset(void) +{ + uint_t i = 0; + for (i = 0; i < AUBIO_LOG_LAST_LEVEL; i++) { + aubio_log_set_level_function(i, aubio_default_log, NULL); + } +} + +aubio_log_function_t +aubio_log_set_level_function(sint_t level, aubio_log_function_t fun, void * data) +{ + aubio_log_function_t old = NULL; + if ((level >= 0) && (level < AUBIO_LOG_LAST_LEVEL)) { + old = aubio_log_function[level]; + aubio_log_function[level] = fun; + aubio_log_user_data[level] = data; + } + return old; +} + +void +aubio_log_set_function(aubio_log_function_t fun, void * data) { + uint_t i = 0; + for (i = 0; i < AUBIO_LOG_LAST_LEVEL; i++) { + aubio_log_set_level_function(i, fun, data); + } +} diff --git a/src/utils/log.h b/src/utils/log.h new file mode 100644 index 00000000..091e91d4 --- /dev/null +++ b/src/utils/log.h @@ -0,0 +1,99 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +#ifndef AUBIO_LOG_H +#define AUBIO_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Logging features + + This file specifies ::aubio_log_set_function and + ::aubio_log_set_level_function, which let you define one or several custom + logging functions to redirect warnings and errors from aubio to your + application. The custom function should have the prototype defined in + ::aubio_log_function_t. + + After a call to ::aubio_log_set_level_function, ::aubio_log_reset can be used + to reset each logging functions to the default ones. + + \example utils/test-log.c + +*/ + +/** list of logging levels */ +enum aubio_log_level { + AUBIO_LOG_ERR, /**< critical errors */ + AUBIO_LOG_INF, /**< infos */ + AUBIO_LOG_MSG, /**< general messages */ + AUBIO_LOG_DBG, /**< debug messages */ + AUBIO_LOG_WRN, /**< warnings */ + AUBIO_LOG_LAST_LEVEL, /**< number of valid levels */ +}; + +/** Logging function prototype, to be passed to ::aubio_log_set_function + + \param level log level + \param message text to log + \param data optional closure used by the callback + + See @ref utils/test-log.c for an example of logging function. + + */ +typedef void (*aubio_log_function_t)(sint_t level, const char_t *message, void + *data); + +/** Set logging function for all levels + + \param fun the function to be used to log, of type ::aubio_log_function_t + \param data optional closure to be passed to the function (can be NULL if + nothing to pass) + + */ +void aubio_log_set_function(aubio_log_function_t fun, void* data); + +/** Set logging function for a given level + + \param level the level for which to set the logging function + \param fun the function to be used to log, of type ::aubio_log_function_t + \param data optional closure to be passed to the function (can be NULL if + nothing to pass) + +*/ +aubio_log_function_t aubio_log_set_level_function(sint_t level, + aubio_log_function_t fun, void* data); + +/** Reset all logging functions to the default one + + After calling this function, the default logging function will be used to + print error, warning, normal, and debug messages to `stdout` or `stderr`. + + */ +void aubio_log_reset(void); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_LOG_H */ diff --git a/src/utils/parameter.c b/src/utils/parameter.c index cd4ea1c0..00b39799 100644 --- a/src/utils/parameter.c +++ b/src/utils/parameter.c @@ -18,7 +18,6 @@ */ -#include "config.h" #include "aubio_priv.h" #include "parameter.h" @@ -84,7 +83,7 @@ uint_t aubio_parameter_set_current_value ( aubio_parameter_t * param, smpl_t val return err; } -smpl_t aubio_parameter_get_current_value ( aubio_parameter_t * s ) +smpl_t aubio_parameter_get_current_value ( const aubio_parameter_t * s ) { return s->current_value; } @@ -109,7 +108,7 @@ uint_t aubio_parameter_set_steps ( aubio_parameter_t * param, uint_t steps ) return AUBIO_OK; } -uint_t aubio_parameter_get_steps ( aubio_parameter_t * param ) +uint_t aubio_parameter_get_steps ( const aubio_parameter_t * param ) { return param->steps; } @@ -120,7 +119,7 @@ uint_t aubio_parameter_set_min_value ( aubio_parameter_t * param, smpl_t min_val return AUBIO_OK; } -smpl_t aubio_parameter_get_min_value ( aubio_parameter_t * param ) +smpl_t aubio_parameter_get_min_value ( const aubio_parameter_t * param ) { return param->min_value; } @@ -131,7 +130,7 @@ uint_t aubio_parameter_set_max_value ( aubio_parameter_t * param, smpl_t max_val return AUBIO_OK; } -smpl_t aubio_parameter_get_max_value ( aubio_parameter_t * param ) +smpl_t aubio_parameter_get_max_value ( const aubio_parameter_t * param ) { return param->max_value; } diff --git a/src/utils/parameter.h b/src/utils/parameter.h index ef296d08..9649eb8c 100644 --- a/src/utils/parameter.h +++ b/src/utils/parameter.h @@ -18,8 +18,8 @@ */ -#ifndef _AUBIO_PARAMETER_H -#define _AUBIO_PARAMETER_H +#ifndef AUBIO_PARAMETER_H +#define AUBIO_PARAMETER_H /** \file @@ -76,7 +76,7 @@ smpl_t aubio_parameter_get_next_value ( aubio_parameter_t * param ); \return current value */ -smpl_t aubio_parameter_get_current_value ( aubio_parameter_t * param ); +smpl_t aubio_parameter_get_current_value ( const aubio_parameter_t * param ); /** set current parameter value, skipping interpolation @@ -105,7 +105,7 @@ uint_t aubio_parameter_set_steps ( aubio_parameter_t * param, uint_t steps ); \return number of steps */ -uint_t aubio_parameter_get_steps ( aubio_parameter_t * param); +uint_t aubio_parameter_get_steps ( const aubio_parameter_t * param); /** set minimum value of this parameter @@ -124,7 +124,7 @@ uint_t aubio_parameter_set_min_value ( aubio_parameter_t * param, smpl_t min_val \return minimum value */ -smpl_t aubio_parameter_get_min_value ( aubio_parameter_t * param ); +smpl_t aubio_parameter_get_min_value ( const aubio_parameter_t * param ); /** set maximum value of this parameter @@ -143,7 +143,7 @@ uint_t aubio_parameter_set_max_value ( aubio_parameter_t * param, smpl_t max_val \return maximum value */ -smpl_t aubio_parameter_get_max_value ( aubio_parameter_t * param ); +smpl_t aubio_parameter_get_max_value ( const aubio_parameter_t * param ); /** destroy ::aubio_parameter_t object @@ -156,4 +156,4 @@ void del_aubio_parameter( aubio_parameter_t * param ); } #endif -#endif /* _AUBIO_PARAMETER_H */ +#endif /* AUBIO_PARAMETER_H */ diff --git a/src/utils/scale.c b/src/utils/scale.c index 83933f82..3bd116c8 100644 --- a/src/utils/scale.c +++ b/src/utils/scale.c @@ -37,7 +37,7 @@ struct _aubio_scale_t { */ }; -aubio_scale_t * new_aubio_scale (smpl_t ilow, smpl_t ihig, +aubio_scale_t * new_aubio_scale (smpl_t ilow, smpl_t ihig, smpl_t olow, smpl_t ohig) { aubio_scale_t * s = AUBIO_NEW(aubio_scale_t); aubio_scale_set_limits (s, ilow, ihig, olow, ohig); diff --git a/src/utils/scale.h b/src/utils/scale.h index abcf53b7..da311140 100644 --- a/src/utils/scale.h +++ b/src/utils/scale.h @@ -28,8 +28,8 @@ \f$ y = (x - ilow)*(ohig-olow)/(ihig-ilow) + olow \f$ */ -#ifndef _AUBIO_SCALE_H -#define _AUBIO_SCALE_H +#ifndef AUBIO_SCALE_H +#define AUBIO_SCALE_H #ifdef __cplusplus extern "C" { @@ -77,4 +77,4 @@ uint_t aubio_scale_set_limits (aubio_scale_t *s, smpl_t ilow, smpl_t ihig, } #endif -#endif /* _AUBIO_SCALE_H */ +#endif /* AUBIO_SCALE_H */ diff --git a/src/utils/windll.c b/src/utils/windll.c new file mode 100644 index 00000000..fce588e4 --- /dev/null +++ b/src/utils/windll.c @@ -0,0 +1,59 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +/** @file + + Windows dll entry point. + +*/ + +#include "aubio_priv.h" + +#ifdef HAVE_WIN_HACKS + +#ifndef __GNUC__ // do not include msvc headers when using gcc/mingw32 + +// latest version +#include +// for earlier versions, include WinSDKVer.h and set _WIN32_WINNT macro + +#endif /* __GNUC__ */ + +#define WIN32_LEAN_AND_MEAN +#include + +#include "aubio.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +#endif diff --git a/src/vecutils.c b/src/vecutils.c index 8e49f96b..8907b5ec 100644 --- a/src/vecutils.c +++ b/src/vecutils.c @@ -1,4 +1,3 @@ -#include "config.h" #include "aubio_priv.h" #include "types.h" #include "fvec.h" diff --git a/src/vecutils.h b/src/vecutils.h index d2cb119b..f0ed96e5 100644 --- a/src/vecutils.h +++ b/src/vecutils.h @@ -24,8 +24,8 @@ */ -#ifndef _AUBIO__VECUTILS_H -#define _AUBIO__VECUTILS_H +#ifndef AUBIO_VECUTILS_H +#define AUBIO_VECUTILS_H #ifdef __cplusplus extern "C" { @@ -113,4 +113,4 @@ void fvec_pow (fvec_t *s, smpl_t pow); } #endif -#endif /* _AUBIO__VECUTILS_H */ +#endif /* AUBIO_VECUTILS_H */ diff --git a/src/wscript_build b/src/wscript_build index a5030c3a..62a8b705 100644 --- a/src/wscript_build +++ b/src/wscript_build @@ -1,6 +1,7 @@ # vim:set syntax=python: uselib = [] +uselib += ['M'] uselib += ['FFTW3', 'FFTW3F'] uselib += ['SAMPLERATE'] uselib += ['SNDFILE'] @@ -10,32 +11,40 @@ uselib += ['AVRESAMPLE'] uselib += ['AVUTIL'] uselib += ['BLAS'] -# build each source files source = ctx.path.ant_glob('*.c **/*.c') + ctx(features = 'c', source = source, includes = ['.'], - uselib = uselib, - lib = 'm', + use = uselib, target = 'lib_objects') # build libaubio.so (cshlib) and/or libaubio.a (cstlib) if ctx.env['DEST_OS'] in ['ios', 'iosimulator']: build_features = ['cstlib', 'cshlib'] elif ctx.env['DEST_OS'] in ['win32', 'win64']: - build_features = ['cshlib'] -else: #linux, darwin, android, mingw, ... - build_features = ['cshlib', 'cstlib'] + build_features = ['cstlib', 'cshlib'] +elif ctx.env['DEST_OS'] in ['emscripten']: + build_features = ['cstlib'] +elif '--static' in ctx.env['LDFLAGS'] or '--static' in ctx.env['LINKFLAGS']: + # static in cflags, ... + build_features = ['cstlib'] +else: + # linux, darwin, android, mingw, ... + build_features = ['cstlib', 'cshlib'] + +# also install static lib +from waflib.Tools.c import cstlib +from waflib.Tools.fc import fcstlib +fcstlib.inst_to = cstlib.inst_to = '${LIBDIR}' for target in build_features: ctx(features = 'c ' + target, - use = ['lib_objects'], - uselib = uselib, - lib = 'm', + use = uselib + ['lib_objects'], target = 'aubio', vnum = ctx.env['LIB_VERSION']) # install headers, except _priv.h ones -ctx.install_files('${PREFIX}/include/aubio/', +ctx.install_files('${INCLUDEDIR}/aubio/', ctx.path.ant_glob('**/*.h', excl = ['**_priv.h', 'config.h']), relative_trick=True) diff --git a/tests/src/io/test-sink_sndfile-multi.c b/tests/src/io/test-sink_sndfile-multi.c index 4dcc6909..46882f6d 100644 --- a/tests/src/io/test-sink_sndfile-multi.c +++ b/tests/src/io/test-sink_sndfile-multi.c @@ -1,6 +1,5 @@ #define AUBIO_UNSTABLE 1 #include -#include "config.h" #include "utils_tests.h" // this file uses the unstable aubio api, please use aubio_sink instead diff --git a/tests/src/io/test-sink_sndfile.c b/tests/src/io/test-sink_sndfile.c index 78122296..fe95310a 100644 --- a/tests/src/io/test-sink_sndfile.c +++ b/tests/src/io/test-sink_sndfile.c @@ -1,6 +1,5 @@ #define AUBIO_UNSTABLE 1 #include -#include "config.h" #include "utils_tests.h" // this file uses the unstable aubio api, please use aubio_sink instead diff --git a/tests/src/io/test-source.c b/tests/src/io/test-source.c index ae1e2016..a1f17aeb 100644 --- a/tests/src/io/test-source.c +++ b/tests/src/io/test-source.c @@ -33,6 +33,8 @@ int main (int argc, char **argv) if (!s) { err = 1; goto beach; } fvec_t *vec = new_fvec(hop_size); + uint_t n_frames_expected = aubio_source_get_duration(s); + samplerate = aubio_source_get_samplerate(s); do { @@ -41,8 +43,9 @@ int main (int argc, char **argv) n_frames += read; } while ( read == hop_size ); - PRINT_MSG("read %d frames at %dHz (%d blocks) from %s\n", n_frames, samplerate, - n_frames / hop_size, source_path); + PRINT_MSG("read %d frames (expected %d) at %dHz (%d blocks) from %s\n", + n_frames, n_frames_expected, samplerate, n_frames / hop_size, + source_path); // close the file (optional) aubio_source_close(s); diff --git a/tests/src/io/test-source_apple_audio.c b/tests/src/io/test-source_apple_audio.c index 6d8ca8c7..92d0e542 100644 --- a/tests/src/io/test-source_apple_audio.c +++ b/tests/src/io/test-source_apple_audio.c @@ -38,6 +38,8 @@ int main (int argc, char **argv) if (!s) { err = 1; goto beach; } fvec_t *vec = new_fvec(hop_size); + uint_t n_frames_expected = aubio_source_apple_audio_get_duration(s); + samplerate = aubio_source_apple_audio_get_samplerate(s); do { @@ -46,8 +48,9 @@ int main (int argc, char **argv) n_frames += read; } while ( read == hop_size ); - PRINT_MSG("read %d frames at %dHz (%d blocks) from %s\n", n_frames, samplerate, - n_frames / hop_size, source_path); + PRINT_MSG("read %d frames (expected %d) at %dHz (%d blocks) from %s\n", + n_frames, n_frames_expected, samplerate, n_frames / hop_size, + source_path); del_fvec (vec); del_aubio_source_apple_audio (s); diff --git a/tests/src/io/test-source_avcodec.c b/tests/src/io/test-source_avcodec.c index b07c9414..dc23d76c 100644 --- a/tests/src/io/test-source_avcodec.c +++ b/tests/src/io/test-source_avcodec.c @@ -38,6 +38,8 @@ int main (int argc, char **argv) if (!s) { err = 1; goto beach; } fvec_t *vec = new_fvec(hop_size); + uint_t n_frames_expected = aubio_source_avcodec_get_duration(s); + samplerate = aubio_source_avcodec_get_samplerate(s); do { @@ -46,8 +48,9 @@ int main (int argc, char **argv) n_frames += read; } while ( read == hop_size ); - PRINT_MSG("read %d frames at %dHz (%d blocks) from %s\n", n_frames, samplerate, - n_frames / hop_size, source_path); + PRINT_MSG("read %d frames (expected %d) at %dHz (%d blocks) from %s\n", + n_frames, n_frames_expected, samplerate, n_frames / hop_size, + source_path); del_fvec (vec); del_aubio_source_avcodec (s); diff --git a/tests/src/io/test-source_sndfile.c b/tests/src/io/test-source_sndfile.c index ed227fd8..6dfff59d 100644 --- a/tests/src/io/test-source_sndfile.c +++ b/tests/src/io/test-source_sndfile.c @@ -38,6 +38,8 @@ int main (int argc, char **argv) if (!s) { err = 1; goto beach; } fvec_t *vec = new_fvec(hop_size); + uint_t n_frames_expected = aubio_source_sndfile_get_duration(s); + samplerate = aubio_source_sndfile_get_samplerate(s); do { @@ -46,8 +48,9 @@ int main (int argc, char **argv) n_frames += read; } while ( read == hop_size ); - PRINT_MSG("read %d frames at %dHz (%d blocks) from %s\n", n_frames, samplerate, - n_frames / hop_size, source_path); + PRINT_MSG("read %d frames (expected %d) at %dHz (%d blocks) from %s\n", + n_frames, n_frames_expected, samplerate, n_frames / hop_size, + source_path); del_fvec (vec); del_aubio_source_sndfile (s); diff --git a/tests/src/io/test-source_wavread.c b/tests/src/io/test-source_wavread.c index 50da5894..23511cac 100644 --- a/tests/src/io/test-source_wavread.c +++ b/tests/src/io/test-source_wavread.c @@ -35,9 +35,12 @@ int main (int argc, char **argv) aubio_source_wavread_t * s = new_aubio_source_wavread(source_path, samplerate, hop_size); + if (!s) { err = 1; goto beach; } fvec_t *vec = new_fvec(hop_size); + uint_t n_frames_expected = aubio_source_wavread_get_duration(s); + samplerate = aubio_source_wavread_get_samplerate(s); do { @@ -46,8 +49,9 @@ int main (int argc, char **argv) n_frames += read; } while ( read == hop_size ); - PRINT_MSG("read %d frames at %dHz (%d blocks) from %s\n", n_frames, samplerate, - n_frames / hop_size, source_path); + PRINT_MSG("read %d frames (expected %d) at %dHz (%d blocks) from %s\n", + n_frames, n_frames_expected, samplerate, n_frames / hop_size, + source_path); del_fvec (vec); del_aubio_source_wavread (s); diff --git a/tests/src/spectral/test-fft.c b/tests/src/spectral/test-fft.c index 72db5302..4d5a5fbb 100644 --- a/tests/src/spectral/test-fft.c +++ b/tests/src/spectral/test-fft.c @@ -4,7 +4,7 @@ int main (void) { int return_code = 0; uint_t i, n_iters = 100; // number of iterations - uint_t win_s = 500; // window size + uint_t win_s = 512; // window size fvec_t * in = new_fvec (win_s); // input buffer cvec_t * fftgrain = new_cvec (win_s); // fft norm and phase fvec_t * out = new_fvec (win_s); // output buffer diff --git a/tests/src/spectral/test-phasevoc.c b/tests/src/spectral/test-phasevoc.c index e43b881d..12af4e6c 100644 --- a/tests/src/spectral/test-phasevoc.c +++ b/tests/src/spectral/test-phasevoc.c @@ -28,7 +28,7 @@ int main (void) // ... cvec_print (fftgrain); - // optionnaly rebuild the signa + // optionally rebuild the signal aubio_pvoc_rdo(pv,fftgrain,out); // and do something with the result diff --git a/tests/src/tempo/test-tempo.c b/tests/src/tempo/test-tempo.c index fc066e10..7a878326 100644 --- a/tests/src/tempo/test-tempo.c +++ b/tests/src/tempo/test-tempo.c @@ -27,7 +27,7 @@ int main (int argc, char **argv) // create some vectors fvec_t * in = new_fvec (hop_size); // input audio buffer - fvec_t * out = new_fvec (2); // output position + fvec_t * out = new_fvec (1); // output position // create tempo object aubio_tempo_t * o = new_aubio_tempo("default", win_size, hop_size, samplerate); diff --git a/tests/src/temporal/test-a_weighting.c b/tests/src/temporal/test-a_weighting.c index f6399b5c..4030a0db 100644 --- a/tests/src/temporal/test-a_weighting.c +++ b/tests/src/temporal/test-a_weighting.c @@ -20,16 +20,22 @@ int main (void) // samplerate unknown f = new_aubio_filter_a_weighting (4200); - del_aubio_filter (f); + if (!f) { + //PRINT_MSG ("failed creating A-weighting filter with samplerate=4200Hz\n"); + } // order to small f = new_aubio_filter (2); - aubio_filter_set_a_weighting (f, samplerate); + if (aubio_filter_set_a_weighting (f, samplerate) != 0) { + //PRINT_MSG ("failed setting filter to A-weighting\n"); + } del_aubio_filter (f); // order to big f = new_aubio_filter (12); - aubio_filter_set_a_weighting (f, samplerate); + if (aubio_filter_set_a_weighting (f, samplerate) != 0) { + //PRINT_MSG ("failed setting filter to A-weighting\n"); + } del_aubio_filter (f); return 0; diff --git a/tests/src/temporal/test-c_weighting.c b/tests/src/temporal/test-c_weighting.c index 96d8ee70..84380c9b 100644 --- a/tests/src/temporal/test-c_weighting.c +++ b/tests/src/temporal/test-c_weighting.c @@ -19,16 +19,22 @@ int main (void) // samplerate unknown f = new_aubio_filter_c_weighting (4200); - del_aubio_filter (f); + if (!f) { + //PRINT_WRN ("failed creating C-weighting filter with samplerate=4200Hz"); + } // order to small f = new_aubio_filter (2); - aubio_filter_set_c_weighting (f, samplerate); + if (aubio_filter_set_c_weighting (f, samplerate) != 0) { + //PRINT_WRN ("failed setting filter to C-weighting"); + } del_aubio_filter (f); // order to big f = new_aubio_filter (12); - aubio_filter_set_c_weighting (f, samplerate); + if (aubio_filter_set_c_weighting (f, samplerate) != 0) { + //PRINT_WRN ("failed setting filter to C-weighting"); + } del_aubio_filter (f); return 0; diff --git a/tests/src/test-lvec.c b/tests/src/test-lvec.c index b1a2e0fb..17b5fc66 100644 --- a/tests/src/test-lvec.c +++ b/tests/src/test-lvec.c @@ -6,7 +6,7 @@ int main (void) uint_t win_s = 32; // window size lvec_t * sp = new_lvec (win_s); // input buffer lvec_set_sample (sp, 2./3., 0); - PRINT_MSG("%lf\n", lvec_get_sample (sp, 0)); + PRINT_MSG(AUBIO_LSMP_FMT "\n", lvec_get_sample (sp, 0)); lvec_print (sp); lvec_ones (sp); lvec_print (sp); diff --git a/tests/src/utils/test-log.c b/tests/src/utils/test-log.c new file mode 100644 index 00000000..1c6b15b3 --- /dev/null +++ b/tests/src/utils/test-log.c @@ -0,0 +1,60 @@ +#include +#include +#include "aubio_priv.h" + +const char_t *hdr = "CUSTOM HEADER: "; +const char_t *hdr2 = "OTHER HEADER: "; + +/* an example of logging function that adds a custom header and prints + * aubio debug messages on stdout instead of stderr */ +void logging(int level, const char_t *message, void *data) { + FILE *out; + //fprintf(stdout, "using custom logging function\n"); + if (level == AUBIO_LOG_ERR) { + out = stderr; + } else { + out = stdout; + } + if ((level >= 0) && (data != NULL)) { + fprintf(out, "%s", (const char_t *)data); + } + fprintf(out, "%s", message); +} + +int main (void) +{ + fprintf(stdout, "### testing normal logging\n"); + AUBIO_ERR("testing normal AUBIO_LOG_ERR\n"); + AUBIO_INF("testing normal AUBIO_LOG_INF\n"); + AUBIO_WRN("testing normal AUBIO_LOG_WRN\n"); + AUBIO_MSG("testing normal AUBIO_LOG_MSG\n"); + AUBIO_DBG("testing normal AUBIO_LOG_DBG\n"); + + fprintf(stdout, "### testing with one custom function\n"); + aubio_log_set_function(logging, (void *)hdr); + AUBIO_ERR("testing custom set_function AUBIO_LOG_ERR\n"); + AUBIO_INF("testing custom set_function AUBIO_LOG_INF\n"); + AUBIO_WRN("testing custom set_function AUBIO_LOG_WRN\n"); + AUBIO_MSG("testing custom set_function AUBIO_LOG_MSG\n"); + AUBIO_DBG("testing custom set_function AUBIO_LOG_DBG\n"); + + fprintf(stdout, "### testing resetted logging\n"); + aubio_log_reset(); + AUBIO_ERR("testing again normal AUBIO_LOG_ERR\n"); + AUBIO_INF("testing again normal AUBIO_LOG_INF\n"); + AUBIO_WRN("testing again normal AUBIO_LOG_WRN\n"); + AUBIO_MSG("testing again normal AUBIO_LOG_MSG\n"); + AUBIO_DBG("testing again normal AUBIO_LOG_DBG\n"); + + fprintf(stdout, "### testing per level customization\n"); + aubio_log_set_level_function(AUBIO_LOG_ERR, logging, (void *)hdr2); + aubio_log_set_level_function(AUBIO_LOG_WRN, logging, NULL); + aubio_log_set_level_function(AUBIO_LOG_MSG, logging, (void *)hdr); + AUBIO_ERR("testing custom set_level_function AUBIO_LOG_ERR\n"); + AUBIO_INF("testing again normal AUBIO_LOG_INF\n"); + AUBIO_WRN("testing custom set_level_function AUBIO_LOG_WRN with data=NULL\n"); + AUBIO_MSG("testing custom set_level_function AUBIO_LOG_MSG\n"); + AUBIO_DBG("testing again normal AUBIO_LOG_DBG\n"); + + return 0; +} diff --git a/tests/utils_tests.h b/tests/utils_tests.h index 07f07196..97d5297a 100644 --- a/tests/utils_tests.h +++ b/tests/utils_tests.h @@ -5,12 +5,28 @@ #include #include "config.h" +#ifdef HAVE_C99_VARARGS_MACROS +#define PRINT_ERR(...) fprintf(stderr, "AUBIO-TESTS ERROR: " __VA_ARGS__) +#define PRINT_MSG(...) fprintf(stdout, __VA_ARGS__) +#define PRINT_DBG(...) fprintf(stderr, __VA_ARGS__) +#define PRINT_WRN(...) fprintf(stderr, "AUBIO-TESTS WARNING: " __VA_ARGS__) +#else #define PRINT_ERR(format, args...) fprintf(stderr, "AUBIO-TESTS ERROR: " format , ##args) #define PRINT_MSG(format, args...) fprintf(stdout, format , ##args) #define PRINT_DBG(format, args...) fprintf(stderr, format , ##args) #define PRINT_WRN(format, args...) fprintf(stderr, "AUBIO-TESTS WARNING: " format, ##args) +#endif + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +#ifndef RAND_MAX +#define RAND_MAX 32767 +#endif -#ifdef HAVE_WIN_HACKS +// are we on windows ? or are we using -std=c99 ? +#if defined(HAVE_WIN_HACKS) || defined(__STRICT_ANSI__) // http://en.wikipedia.org/wiki/Linear_congruential_generator // no srandom/random on win32 diff --git a/tests/wscript_build b/tests/wscript_build index 55dcbf24..45e83b8e 100644 --- a/tests/wscript_build +++ b/tests/wscript_build @@ -1,26 +1,19 @@ # vim:set syntax=python: -for target_name in ctx.path.ant_glob('src/**/*.c'): - uselib = [] - uselib += ['FFTW3', 'FFTW3F'] - uselib += ['SAMPLERATE'] - uselib += ['SNDFILE'] - uselib += ['AVCODEC'] - uselib += ['AVFORMAT'] - uselib += ['AVRESAMPLE'] - uselib += ['AVUTIL'] - uselib += ['JACK'] - uselib += ['BLAS'] - includes = ['../src', '.'] - extra_source = [] +import os.path +uselib = ['aubio'] + +includes = ['../src', '.'] +programs_sources = ctx.path.ant_glob('src/**/*.c') + +for source_file in programs_sources: + target = os.path.basename(os.path.splitext(str(source_file))[0]) bld(features = 'c cprogram test', - lib = 'm', - uselib = uselib, - source = [target_name] + extra_source, - target = str(target_name).split('.')[0], + source = source_file, + target = target, includes = includes, + use = uselib, install_path = None, defines = 'AUBIO_UNSTABLE_API=1', - cflags = ['-g'], - use = 'aubio') + ) diff --git a/wscript b/wscript index 0a506010..f8e9cc09 100644 --- a/wscript +++ b/wscript @@ -10,6 +10,8 @@ # have Python installed, you do *not* need to install anything to build aubio. # For more info about waf, see http://code.google.com/p/waf/ . +import sys + APPNAME = 'aubio' # source VERSION @@ -45,6 +47,12 @@ def add_option_enable_disable(ctx, name, default = None, help = help_disable_str ) def options(ctx): + ctx.add_option('--build-type', action = 'store', + default = "release", + choices = ('debug', 'release'), + dest = 'build_type', + help = 'whether to compile with (--build-type=release) or without (--build-type=debug) '\ + ' compiler opimizations [default: release]') add_option_enable_disable(ctx, 'fftw3f', default = False, help_str = 'compile with fftw3f instead of ooura (recommended)', help_disable_str = 'do not compile with fftw3f') @@ -81,9 +89,19 @@ def options(ctx): add_option_enable_disable(ctx, 'apple-audio', default = None, help_str = 'use CoreFoundation (darwin only) (auto)', help_disable_str = 'do not use CoreFoundation framework') - add_option_enable_disable(ctx, 'atlas', default = None, - help_str = 'use Atlas library (auto)', + add_option_enable_disable(ctx, 'atlas', default = False, + help_str = 'use Atlas library (no)', help_disable_str = 'do not use Atlas library') + add_option_enable_disable(ctx, 'wavread', default = True, + help_str = 'compile with source_wavread (default)', + help_disable_str = 'do not compile source_wavread') + add_option_enable_disable(ctx, 'wavwrite', default = True, + help_str = 'compile with source_wavwrite (default)', + help_disable_str = 'do not compile source_wavwrite') + + add_option_enable_disable(ctx, 'docs', default = None, + help_str = 'build documentation (auto)', + help_disable_str = 'do not build documentation') ctx.add_option('--with-target-platform', type='string', help='set target platform for cross-compilation', dest='target_platform') @@ -98,15 +116,50 @@ def configure(ctx): ctx.load('waf_unit_test') ctx.load('gnu_dirs') - target_platform = Options.platform + # check for common headers + ctx.check(header_name='stdlib.h') + ctx.check(header_name='stdio.h') + ctx.check(header_name='math.h') + ctx.check(header_name='string.h') + ctx.check(header_name='limits.h') + ctx.check(header_name='stdarg.h') + ctx.check(header_name='getopt.h', mandatory = False) + ctx.check(header_name='unistd.h', mandatory = False) + + target_platform = sys.platform if ctx.options.target_platform: target_platform = ctx.options.target_platform ctx.env['DEST_OS'] = target_platform - if 'CL.exe' not in ctx.env.CC[0]: - ctx.env.CFLAGS += ['-g', '-Wall', '-Wextra'] + if ctx.options.build_type == "debug": + ctx.define('DEBUG', 1) + else: + ctx.define('NDEBUG', 1) + + if ctx.env.CC_NAME != 'msvc': + if ctx.options.build_type == "debug": + # no optimization in debug mode + ctx.env.prepend_value('CFLAGS', ['-O0']) + else: + # default to -O2 in release mode + ctx.env.prepend_value('CFLAGS', ['-O2']) + # enable debug symbols and configure warnings + ctx.env.prepend_value('CFLAGS', ['-g', '-Wall', '-Wextra']) else: - ctx.env.CFLAGS += ['-Wall'] + # enable debug symbols + ctx.env.CFLAGS += ['/Z7', '/FS'] + ctx.env.LINKFLAGS += ['/DEBUG', '/INCREMENTAL:NO'] + # configure warnings + ctx.env.CFLAGS += ['/W4', '/D_CRT_SECURE_NO_WARNINGS'] + # set optimization level and runtime libs + if (ctx.options.build_type == "release"): + ctx.env.CFLAGS += ['/Ox'] + ctx.env.CFLAGS += ['/MD'] + else: + assert(ctx.options.build_type == "debug") + ctx.env.CFLAGS += ['/MDd'] + + ctx.check_cc(lib='m', uselib_store='M', mandatory=False) if target_platform not in ['win32', 'win64']: ctx.env.CFLAGS += ['-fPIC'] @@ -117,15 +170,24 @@ def configure(ctx): if target_platform == 'darwin' and ctx.options.enable_fat: ctx.env.CFLAGS += ['-arch', 'i386', '-arch', 'x86_64'] ctx.env.LINKFLAGS += ['-arch', 'i386', '-arch', 'x86_64'] + MINSDKVER="10.4" + ctx.env.CFLAGS += [ '-mmacosx-version-min=' + MINSDKVER ] + ctx.env.LINKFLAGS += [ '-mmacosx-version-min=' + MINSDKVER ] if target_platform in [ 'darwin', 'ios', 'iosimulator']: if (ctx.options.enable_apple_audio != False): ctx.env.FRAMEWORK += ['CoreFoundation', 'AudioToolbox'] ctx.define('HAVE_SOURCE_APPLE_AUDIO', 1) ctx.define('HAVE_SINK_APPLE_AUDIO', 1) + ctx.msg('Checking for AudioToolbox.framework', 'yes') + else: + ctx.msg('Checking for AudioToolbox.framework', 'no (disabled)', color = 'YELLOW') if (ctx.options.enable_accelerate != False): ctx.define('HAVE_ACCELERATE', 1) ctx.env.FRAMEWORK += ['Accelerate'] + ctx.msg('Checking for Accelerate framework', 'yes') + else: + ctx.msg('Checking for Accelerate framework', 'no (disabled)', color = 'YELLOW') if target_platform in [ 'ios', 'iosimulator' ]: MINSDKVER="6.1" @@ -159,12 +221,13 @@ def configure(ctx): ctx.env.CFLAGS += [ '-isysroot' , SDKROOT] ctx.env.LINKFLAGS += [ '-isysroot' , SDKROOT] - # check for required headers - ctx.check(header_name='stdlib.h') - ctx.check(header_name='stdio.h') - ctx.check(header_name='math.h') - ctx.check(header_name='string.h') - ctx.check(header_name='limits.h') + if target_platform == 'emscripten': + import os.path + ctx.env.CFLAGS += [ '-I' + os.path.join(os.environ['EMSCRIPTEN'], 'system', 'include') ] + ctx.env.CFLAGS += ['-Oz'] + ctx.env.cprogram_PATTERN = "%s.js" + if (ctx.options.enable_atlas != True): + ctx.options.enable_atlas = False # check support for C99 __VA_ARGS__ macros check_c99_varargs = ''' @@ -178,15 +241,19 @@ def configure(ctx): mandatory = False): ctx.define('HAVE_C99_VARARGS_MACROS', 1) - # double precision mode + # show a message about enable_double status if (ctx.options.enable_double == True): - ctx.define('HAVE_AUBIO_DOUBLE', 1) + ctx.msg('Checking for size of smpl_t', 'double') + ctx.msg('Checking for size of lsmp_t', 'long double') else: - ctx.define('HAVE_AUBIO_DOUBLE', 0) + ctx.msg('Checking for size of smpl_t', 'float') + ctx.msg('Checking for size of lsmp_t', 'double') # optionally use complex.h if (ctx.options.enable_complex == True): ctx.check(header_name='complex.h') + else: + ctx.msg('Checking if complex.h is enabled', 'no') # check for fftw3 if (ctx.options.enable_fftw3 != False or ctx.options.enable_fftw3f != False): @@ -228,6 +295,13 @@ def configure(ctx): mandatory = ctx.options.enable_sndfile) # check for libsamplerate + if (ctx.options.enable_double): + if (ctx.options.enable_samplerate): + ctx.fatal("Could not compile aubio in double precision mode with libsamplerate") + else: + ctx.options.enable_samplerate = False + ctx.msg('Checking if using samplerate', 'no (disabled in double precision mode)', + color = 'YELLOW') if (ctx.options.enable_samplerate != False): ctx.check_cfg(package = 'samplerate', atleast_version = '0.0.15', args = '--cflags --libs', @@ -260,8 +334,12 @@ def configure(ctx): else: ctx.msg('Checking for all libav libraries', 'not found', color = 'YELLOW') - ctx.define('HAVE_WAVREAD', 1) - ctx.define('HAVE_WAVWRITE', 1) + if (ctx.options.enable_wavread != False): + ctx.define('HAVE_WAVREAD', 1) + ctx.msg('Checking if using source_wavread', ctx.options.enable_wavread and 'yes' or 'no') + if (ctx.options.enable_wavwrite!= False): + ctx.define('HAVE_WAVWRITE', 1) + ctx.msg('Checking if using sink_wavwrite', ctx.options.enable_wavwrite and 'yes' or 'no') # use ATLAS if (ctx.options.enable_atlas != False): @@ -272,47 +350,68 @@ def configure(ctx): # use memcpy hacks if (ctx.options.enable_memcpy == True): ctx.define('HAVE_MEMCPY_HACKS', 1) - else: - ctx.define('HAVE_MEMCPY_HACKS', 0) # write configuration header ctx.write_config_header('src/config.h') + # the following defines will be passed as arguments to the compiler + # instead of being written to src/config.h + ctx.define('HAVE_CONFIG_H', 1) + # add some defines used in examples ctx.define('AUBIO_PREFIX', ctx.env['PREFIX']) ctx.define('PACKAGE', APPNAME) - # check if txt2man is installed, optional - try: - ctx.find_program('txt2man', var='TXT2MAN') - except ctx.errors.ConfigurationError: - ctx.to_log('txt2man was not found (ignoring)') + # double precision mode + if (ctx.options.enable_double == True): + ctx.define('HAVE_AUBIO_DOUBLE', 1) - # check if doxygen is installed, optional - try: - ctx.find_program('doxygen', var='DOXYGEN') - except ctx.errors.ConfigurationError: - ctx.to_log('doxygen was not found (ignoring)') + if (ctx.options.enable_docs != False): + # check if txt2man is installed, optional + try: + ctx.find_program('txt2man', var='TXT2MAN') + except ctx.errors.ConfigurationError: + ctx.to_log('txt2man was not found (ignoring)') + + # check if doxygen is installed, optional + try: + ctx.find_program('doxygen', var='DOXYGEN') + except ctx.errors.ConfigurationError: + ctx.to_log('doxygen was not found (ignoring)') + + # check if sphinx-build is installed, optional + try: + ctx.find_program('sphinx-build', var='SPHINX') + except ctx.errors.ConfigurationError: + ctx.to_log('sphinx-build was not found (ignoring)') def build(bld): bld.env['VERSION'] = VERSION bld.env['LIB_VERSION'] = LIB_VERSION - # add sub directories + # main source bld.recurse('src') - if bld.env['DEST_OS'] not in ['ios', 'iosimulator']: - pass + + # add sub directories if bld.env['DEST_OS'] not in ['ios', 'iosimulator', 'android']: bld.recurse('examples') bld.recurse('tests') + # pkg-config template bld( source = 'aubio.pc.in' ) + # documentation + txt2man(bld) + doxygen(bld) + sphinx(bld) + +def txt2man(bld): # build manpages from txt files using txt2man if bld.env['TXT2MAN']: from waflib import TaskGen if 'MANDIR' not in bld.env: - bld.env['MANDIR'] = bld.env['PREFIX'] + '/share/man' + bld.env['MANDIR'] = bld.env['DATAROOTDIR'] + '/man' + bld.env.VERSION = VERSION rule_str = '${TXT2MAN} -t `basename ${TGT} | cut -f 1 -d . | tr a-z A-Z`' rule_str += ' -r ${PACKAGE}\\ ${VERSION} -P ${PACKAGE}' rule_str += ' -v ${PACKAGE}\\ User\\\'s\\ manual' @@ -327,16 +426,51 @@ def build(bld): ) bld( source = bld.path.ant_glob('doc/*.txt') ) +def doxygen(bld): # build documentation from source files using doxygen if bld.env['DOXYGEN']: bld( name = 'doxygen', rule = 'doxygen ${SRC} > /dev/null', source = 'doc/web.cfg', + target = '../doc/web/html/index.html', cwd = 'doc') - bld.install_files( '${PREFIX}' + '/share/doc/libaubio-doc', + bld.install_files( '${DATAROOTDIR}' + '/doc/libaubio-doc', bld.path.ant_glob('doc/web/html/**'), cwd = bld.path.find_dir ('doc/web'), relative_trick = True) +def sphinx(bld): + # build documentation from source files using sphinx-build + # note: build in ../doc/_build/html, otherwise waf wont install unsigned files + if bld.env['SPHINX']: + bld.env.VERSION = VERSION + bld( name = 'sphinx', + rule = '${SPHINX} -b html -D release=${VERSION} -D version=${VERSION} -a -q `dirname ${SRC}` `dirname ${TGT}`', + source = 'doc/conf.py', + target = '../doc/_build/html/index.html') + bld.install_files( '${DATAROOTDIR}' + '/doc/libaubio-doc/sphinx', + bld.path.ant_glob('doc/_build/html/**'), + cwd = bld.path.find_dir('doc/_build/html'), + relative_trick = True) + +# register the previous rules as build rules +from waflib.Build import BuildContext + +class build_txt2man(BuildContext): + cmd = 'txt2man' + fun = 'txt2man' + +class build_manpages(BuildContext): + cmd = 'manpages' + fun = 'txt2man' + +class build_sphinx(BuildContext): + cmd = 'sphinx' + fun = 'sphinx' + +class build_doxygen(BuildContext): + cmd = 'doxygen' + fun = 'doxygen' + def shutdown(bld): from waflib import Logs if bld.options.target_platform in ['ios', 'iosimulator']: @@ -346,12 +480,25 @@ def shutdown(bld): Logs.pprint('RED', msg) def dist(ctx): - ctx.excl = ' **/.waf-1* **/*~ **/*.pyc **/*.swp **/.lock-w* **/.git*' + ctx.excl = ' **/.waf* **/*~ **/*.pyc **/*.swp **/*.swo **/*.swn **/.lock-w* **/.git*' ctx.excl += ' **/build/*' + ctx.excl += ' doc/_build' + ctx.excl += ' python/demos_*' ctx.excl += ' **/python/gen **/python/build **/python/dist' + ctx.excl += ' **/python/ext/config.h' + ctx.excl += ' **/python/lib/aubio/_aubio.so' + ctx.excl += ' **.egg-info' ctx.excl += ' **/**.zip **/**.tar.bz2' + ctx.excl += ' **.tar.bz2' ctx.excl += ' **/doc/full/* **/doc/web/*' + ctx.excl += ' **/doc/full.cfg' ctx.excl += ' **/python/*.db' ctx.excl += ' **/python.old/*' + ctx.excl += ' **/python/*/*.old' ctx.excl += ' **/python/tests/sounds' ctx.excl += ' **/**.asc' + ctx.excl += ' **/dist*' + ctx.excl += ' **/.DS_Store' + ctx.excl += ' **/.travis.yml' + ctx.excl += ' **/.landscape.yml' + ctx.excl += ' **/.appveyor.yml'