[ci] add pip install to readthedocs.yaml master
authorPaul Brossier <piem@piem.org>
Tue, 2 Jan 2024 20:16:22 +0000 (21:16 +0100)
committerPaul Brossier <piem@piem.org>
Tue, 2 Jan 2024 20:16:48 +0000 (21:16 +0100)
54 files changed:
.appveyor.yml
.circleci/config.yml
.gitignore
.readthedocs.yaml [new file with mode: 0644]
.travis.yml
Makefile
README.md
VERSION
azure-pipelines.yml
doc/develop.rst
doc/installing.rst
doc/requirements.rst
examples/jackio.c
examples/parse_args.h
examples/utils.c
python/demos/demo_pitch.py
python/demos/demo_pitchshift.py [new file with mode: 0755]
python/ext/ufuncs.c
python/lib/gen_code.py
python/lib/gen_external.py
python/lib/moresetuptools.py
python/tests/test_pitchshift.py [new file with mode: 0755]
scripts/build_android
scripts/build_apple_frameworks
scripts/build_emscripten
scripts/build_mingw
scripts/get_waf.sh
src/aubio.h
src/aubio_priv.h
src/effects/pitchshift.h [new file with mode: 0644]
src/effects/pitchshift_dummy.c [new file with mode: 0644]
src/effects/pitchshift_rubberband.c [new file with mode: 0644]
src/effects/rubberband_utils.c [new file with mode: 0644]
src/effects/timestretch.h [new file with mode: 0644]
src/effects/timestretch_dummy.c [new file with mode: 0644]
src/effects/timestretch_rubberband.c [new file with mode: 0644]
src/io/sink.c
src/io/sink_apple_audio.c
src/io/sink_apple_audio.h
src/io/sink_flac.c [new file with mode: 0644]
src/io/sink_sndfile.c
src/io/sink_sndfile.h
src/io/sink_vorbis.c [new file with mode: 0644]
src/io/source_avcodec.c
src/io/utils_apple_audio.c
src/musicutils.h
src/utils/strutils.c [new file with mode: 0644]
src/wscript_build
tests/src/effects/test-pitchshift.c [new file with mode: 0644]
tests/src/effects/test-timestretch.c [new file with mode: 0644]
tests/src/io/test-sink_flac.c [new file with mode: 0644]
tests/src/io/test-sink_vorbis.c [new file with mode: 0644]
tests/src/spectral/test-dct.c
wscript

index 0c21b17..e155006 100644 (file)
@@ -15,14 +15,6 @@ environment:
       PYTHON_VERSION: 2.7.x
       PYTHON_ARCH: 64
 
-    - PYTHONDIR: C:\Python35
-      PYTHON_VERSION: 3.5.x
-      PYTHON_ARCH: 32
-
-    - PYTHONDIR: C:\Python35-x64
-      PYTHON_VERSION: 3.5.x
-      PYTHON_ARCH: 64
-
     - PYTHONDIR: C:\Python36
       PYTHON_VERSION: 3.6.x
       PYTHON_ARCH: 32
@@ -39,6 +31,14 @@ environment:
       PYTHON_VERSION: 3.7.x
       PYTHON_ARCH: 64
 
+    - PYTHONDIR: C:\Python38
+      PYTHON_VERSION: 3.8.x
+      PYTHON_ARCH: 32
+
+    - PYTHONDIR: C:\Python38-x64
+      PYTHON_VERSION: 3.8.x
+      PYTHON_ARCH: 64
+
 install:
   - ECHO "Installed SDKs:"
   - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
index eb00738..b2594c7 100644 (file)
@@ -1,8 +1,12 @@
+# python circleci 2.1 configuration
+# see https://circleci.com/docs/2.1/language-python/
+version: 2.1
+
 apt-run: &apt-install
   name: Install apt packages
   command: |
     sudo apt-get update
-    sudo apt-get -y install make sox pkg-config libavcodec-dev libavformat-dev libavresample-dev libavutil-dev libsndfile1-dev libsamplerate-dev
+    sudo apt-get -y install make sox pkg-config libavcodec-dev libavformat-dev libswresample-dev libavutil-dev libsndfile1-dev libsamplerate-dev librubberband-dev libvorbis-dev libflac-dev
 
 pip-install: &pip-install
   name: Install pip dependencies
@@ -36,11 +40,10 @@ uninstall-wheel: &uninstall-wheel
     pip show -f aubio
     pip uninstall --verbose --yes aubio
 
-version: 2
 jobs:
-  build-27:
+  build-36:
     docker:
-      - image: circleci/python:2.7
+      - image: cimg/python:3.6
     steps:
       - checkout
       - run: *apt-install
@@ -52,9 +55,9 @@ jobs:
       - store_artifacts:
           path: dist/
 
-  build-36:
+  build-310:
     docker:
-      - image: circleci/python:3.6
+      - image: cimg/python:3.10
     steps:
       - checkout
       - run: *apt-install
@@ -66,9 +69,22 @@ jobs:
       - store_artifacts:
           path: dist/
 
-  build-37:
+  build-310-nodeps:
+    docker:
+      - image: cimg/python:3.10
+    steps:
+      - checkout
+      - run: *pip-install
+      - run: *build-wheel
+      - run: *install-wheel
+      - run: *test-pytest-nosounds
+      - run: *uninstall-wheel
+      - store_artifacts:
+          path: dist/
+
+  build-312:
     docker:
-      - image: circleci/python:3.7
+      - image: cimg/python:3.12
     steps:
       - checkout
       - run: *apt-install
@@ -80,9 +96,9 @@ jobs:
       - store_artifacts:
           path: dist/
 
-  build-37-nodeps:
+  build-312-nodeps:
     docker:
-      - image: circleci/python:3.7
+      - image: cimg/python:3.12
     steps:
       - checkout
       - run: *pip-install
@@ -98,7 +114,8 @@ workflows:
 
   test-wheel:
     jobs:
-      - build-27
       - build-36
-      - build-37
-      - build-37-nodeps
+      - build-310
+      - build-310-nodeps
+      - build-312
+      - build-312-nodeps
index 21bb1cc..e17a67d 100644 (file)
@@ -12,8 +12,8 @@ python/lib/aubio/_aubio.*.so
 RE:examples/[a-z]*
 
 # ignore compiled test programs
-RE:tests/src/test-[a-z-_]*$
-RE:tests/cpp/test-[a-z-_]*$
+RE:tests/src/test-[a-z_-]*$
+RE:tests/cpp/test-[a-z_-]*$
 
 # only sgml manpages count
 doc/*.1
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644 (file)
index 0000000..85253fb
--- /dev/null
@@ -0,0 +1,16 @@
+# see https://docs.readthedocs.io/en/stable/config-file/
+version: 2
+
+build:
+  os: ubuntu-22.04
+  tools:
+    python: "3.12"
+
+python:
+  install:
+    - requirements: requirements.txt
+    - method: pip
+      path: .
+
+sphinx:
+  configuration: doc/conf.py
index 788e206..30b81e5 100644 (file)
@@ -2,55 +2,63 @@ language: python
 
 matrix:
   include:
-    - python: 3.6
+    - name: "Linux (Python 3.8)"
+      python: 3.8
       os: linux
-      compiler: gcc
-    - python: 3.5
+      distro: bionic
+    - name: "Linux (Python 3.6)"
+      python: 3.6
       os: linux
-      compiler: gcc
       env: WAFOPTS="--build-type=debug"
-    - python: 2.7
+    - name: "Linux (Python 2.7, nodeps)"
+      python: 2.7
       os: linux
-      compiler: gcc
-    - python: "pypy3.5"
+      distro: trusty
+      env: WAFOPTS="--nodeps"
+    - name: "Linux (Python pypy3.5)"
+      language: python
+      python: "pypy3.5"
       os: linux
-      compiler: gcc
-      env: CFLAGS="-Os" WAFOPTS="--disable-avcodec"
-    - python: 3.6
+    - name: "Linux (double, fftw3)"
+      python: 3.8
       os: linux
-      compiler: gcc
-      env: CFLAGS="-Os" WAFOPTS="--disable-samplerate"
-    - python: 3.5
-      os: linux
-      compiler: gcc
       env: HAVE_AUBIO_DOUBLE=1 CFLAGS="-O3" WAFOPTS="--enable-fftw3"
-    - python: 2.7
+    - name: "Linux (default, dpkg-buildflags)"
       os: linux
-      compiler: gcc
       env: CFLAGS="`dpkg-buildflags --get CFLAGS`" LDFLAGS="`dpkg-buildflags --get LDFLAGS`"
-    - language: C
+
+    - name: "macOS (xcode11)"
+      language: shell
       os: osx
-      compiler: clang
-    - language: C
+      osx_image: xcode11
+    - name: "macOS (xcode12, lib only)"
+      language: shell
       os: osx
-      compiler: clang
-      env: CFLAGS="-Os" HAVE_AUBIO_DOUBLE=1 WAFOPTS="--disable-accelerate"
-    - language: C
+      osx_image: xcode12
+      script:
+        - make test_lib_only_clean
+    - name: "macOS (xcode10, noopt, nodeps)"
+      language: shell
       os: osx
-      compiler: clang
-      env: WAFOPTS="--enable-fat --disable-avcodec --disable-sndfile --disable-samplerate"
-    - language: C
+      osx_image: xcode10
+      env: CFLAGS="-Os" HAVE_AUBIO_DOUBLE=1 WAFOPTS="--disable-accelerate --nodeps"
+    - name: "iOS"
+      language: shell
       os: osx
-      compiler: clang
-      env: WAFOPTS="--with-target-platform=ios --disable-avcodec --disable-sndfile --disable-samplerate" AUBIO_NOTESTS=1
-    - language: C
+      env: WAFOPTS="--with-target-platform=ios --nodeps" AUBIO_NOTESTS=1
+    - name: "iOSSimulator"
+      language: shell
       os: osx
-      compiler: clang
-      env: WAFOPTS="--with-target-platform=iosimulator --disable-avcodec --disable-sndfile --disable-samplerate" AUBIO_NOTESTS=1
+      env: WAFOPTS="--with-target-platform=iosimulator --nodeps" AUBIO_NOTESTS=1
 
-# use trusty
-dist: trusty
-sudo: required
+#    - name: "Windows (Python 3.8.0, lib only)"
+#      language: shell
+#      os: windows
+#      before_install:
+#        - choco install python --version 3.8.0
+#        - choco install make
+#        - python -m pip install --upgrade pip
+#      env: PATH=/c/Python38:/c/Python38/Scripts:$PATH AUBIO_NOTESTS=1
 
 addons:
   apt:
@@ -58,13 +66,16 @@ addons:
     - bzip2
     - libavcodec-dev
     - libavformat-dev
-    - libavresample-dev
+    - libswresample-dev
     - libavutil-dev
     - libsndfile1-dev
     - libsamplerate-dev
+    - libvorbis-dev
+    - libflac-dev
     - libjack-dev
     - libasound2-dev
     - libfftw3-dev
+    - librubberband-dev
     - sox
     - lcov
   homebrew:
@@ -72,22 +83,15 @@ addons:
     - sox
     - ffmpeg
     - libsndfile
+    - libvorbis
+    - flac
+    - libsamplerate
+    - rubberband
     - lcov
     update: true
 
-before_install:
-   - |
-     if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
-       export PATH="$HOME/Library/Python/2.7/bin/:$PATH"
-     fi;
-
 install:
-  - |
-    if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
-      alias pip=pip2
-    fi;
-  - travis_retry pip install --upgrade pip
-  - travis_retry make getwaf expandwaf deps_python
+  - make getwaf deps_python
   - which pip
   - pip --version
   - pip install coverage
index a21c0d6..97d9d19 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -183,7 +183,7 @@ create_test_sounds:
        -$(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 48000 -b 16 -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
 
index 45db911..c3cbabb 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,12 +1,15 @@
 aubio
 =====
 
-[![Travis build status](https://travis-ci.org/aubio/aubio.svg?branch=master)](https://travis-ci.org/aubio/aubio "Travis build status")
+[![CircleCI build status](https://circleci.com/gh/aubio/aubio.svg?style=shield)](https://circleci.com/gh/aubio/aubio "CircleCI build status")
+[![Azure Pipelines](https://dev.azure.com/aubio/aubio/_apis/build/status/aubio.aubio)](https://dev.azure.com/aubio/aubio "Azure build status")
 [![Appveyor build status](https://img.shields.io/appveyor/ci/piem/aubio/master.svg)](https://ci.appveyor.com/project/piem/aubio "Appveyor build status")
-[![Commits since last release](https://img.shields.io/github/commits-since/aubio/aubio/latest.svg)](https://github.com/aubio/aubio "Commits since last release")
+[![Pypi Downloads](https://img.shields.io/pypi/dm/aubio.svg?label=Pypi%20downloads)](https://pypi.org/project/aubio/)
+[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/aubio.svg?label=Conda%20downloads)](https://anaconda.org/conda-forge/aubio)
 
 [![Documentation](https://readthedocs.org/projects/aubio/badge/?version=latest)](http://aubio.readthedocs.io/en/latest/?badge=latest "Latest documentation")
 [![DOI](https://zenodo.org/badge/396389.svg)](https://zenodo.org/badge/latestdoi/396389)
+[![Commits since last release](https://img.shields.io/github/commits-since/aubio/aubio/latest.svg)](https://github.com/aubio/aubio "Commits since last release")
 
 aubio is a library 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
diff --git a/VERSION b/VERSION
index 8fa7caa..5a03d28 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1,7 +1,7 @@
 AUBIO_MAJOR_VERSION=0
 AUBIO_MINOR_VERSION=5
 AUBIO_PATCH_VERSION=0
-AUBIO_VERSION_STATUS='~alpha'
+AUBIO_VERSION_STATUS='-alpha'
 LIBAUBIO_LT_CUR=5
 LIBAUBIO_LT_REV=4
 LIBAUBIO_LT_AGE=8
index 277140c..974dffa 100644 (file)
@@ -3,7 +3,35 @@ jobs:
 
 - job: linux
   pool:
-    vmImage: 'ubuntu-16.04'
+    vmImage: 'ubuntu-latest'
+  steps:
+  - script: |
+      sudo apt update
+      sudo apt install libsndfile-dev libavformat-dev librubberband-dev libvorbis-dev libflac-dev
+    displayName: 'deps'
+  - script: |
+      make
+    displayName: 'make'
+    env:
+      CFLAGS: -Werror
+
+- job: linux_double
+  pool:
+    vmImage: 'ubuntu-latest'
+  steps:
+  - script: |
+      sudo apt update
+      sudo apt install libfftw3-dev libsndfile-dev libavformat-dev librubberband-dev libvorbis-dev libflac-dev
+    displayName: 'deps'
+  - script: |
+      HAVE_AUBIO_DOUBLE=1 WAFOPTS="--enable-fftw3" make
+    displayName: 'make'
+    env:
+      CFLAGS: -Werror
+
+- job: linux_nodeps
+  pool:
+    vmImage: 'ubuntu-latest'
   steps:
   - script: |
       make
@@ -13,7 +41,7 @@ jobs:
 
 - job: windows
   pool:
-    vmImage: 'vs2017-win2016'
+    vmImage: 'windows-latest'
   steps:
   - script: |
       make
@@ -24,12 +52,12 @@ jobs:
 
 - job: macos
   pool:
-    vmImage: 'macos-10.13'
+    vmImage: 'macos-latest'
   steps:
   - script: |
       brew update
       brew install pkg-config gnupg
-      brew install sox ffmpeg libsndfile lcov
+      brew install sox ffmpeg libsndfile libvorbis flac lcov
     displayName: 'brew install'
   - script: |
       make
index fa27db0..3e88987 100644 (file)
@@ -92,7 +92,7 @@ First, define a few variables and allocate some memory.
 
 .. literalinclude:: ../tests/src/io/test-source.c
    :language: C
-   :lines: 22-24, 30-32, 34
+   :lines: 24-26, 30, 32-34
 
 .. note::
    With ``samplerate = 0``, ``aubio_source`` will be created with the file's
@@ -102,13 +102,13 @@ Now for the processing loop:
 
 .. literalinclude:: ../tests/src/io/test-source.c
    :language: C
-   :lines: 40-44
+   :lines: 41-45
 
 At the end of the processing loop, memory is deallocated:
 
 .. literalinclude:: ../tests/src/io/test-source.c
    :language: C
-   :lines: 55-56
+   :lines: 55-58
 
 See the complete example: :download:`test-source.c
 <../tests/src/io/test-source.c>`.
@@ -126,13 +126,13 @@ The processing loop could now look like:
 
 .. literalinclude:: ../tests/src/spectral/test-phasevoc.c
    :language: C
-   :lines: 20-37
+   :lines: 27-44
 
 Time to clean up the previously allocated memory:
 
 .. literalinclude:: ../tests/src/spectral/test-phasevoc.c
    :language: C
-   :lines: 39-44
+   :lines: 47-50
 
 See the complete example: :download:`test-phasevoc.c
 <../tests/src/spectral/test-phasevoc.c>`.
index 2c10412..2044c0b 100644 (file)
@@ -67,8 +67,7 @@ Cheat sheet
 - :ref:`check the list of optional dependencies <requirements>`::
 
     # debian / ubuntu
-    dpkg -l libavcodec-dev libavutil-dev libavformat-dev \
-            libswresample-dev libavresample-dev \
+    dpkg -l libavcodec-dev libavutil-dev libavformat-dev libswresample-dev \
             libsamplerate-dev libsndfile-dev \
             txt2man doxygen
 
index fcad2d1..905855f 100644 (file)
@@ -31,6 +31,8 @@ unusual location.
     If ``pkg-config`` is not found in ``PATH``, the configure step will
     succeed, but none of the external libraries will be used.
 
+To build aubio with no external libraries, use the ``--nodeps`` build option.
+
 Media libraries
 ---------------
 
@@ -48,7 +50,7 @@ from all formats supported by `libav
 * libavcodec
 * libavformat
 * libavutil
-* libavresample
+* libswresample
 
 To enable this option, configure with ``--enable-avcodec``. The build will then
 failed if the required libraries are not found. To disable this option,
index f781fb1..7a67731 100644 (file)
@@ -123,10 +123,10 @@ new_aubio_jack (uint_t hop_size, uint_t ichan, uint_t ochan,
   for (i = 0; i < ochan + omidichan; i++) {
     if (i < ochan) {
       jack_port_type = JACK_DEFAULT_AUDIO_TYPE;
-      AUBIO_SPRINTF (name, "out_%d", i + 1);
+      AUBIO_SNPRINTF (name, sizeof (name), "out_%d", i + 1);
     } else {
       jack_port_type = JACK_DEFAULT_MIDI_TYPE;
-      AUBIO_SPRINTF (name, "midi_out_%d", i - ochan + 1);
+      AUBIO_SNPRINTF (name, sizeof (name), "midi_out_%d", i - ochan + 1);
     }
     if ((jack_setup->oports[i] =
             jack_port_register (jack_setup->client, name,
@@ -140,10 +140,10 @@ new_aubio_jack (uint_t hop_size, uint_t ichan, uint_t ochan,
   for (i = 0; i < ichan + imidichan; i++) {
     if (i < ichan) {
       jack_port_type = JACK_DEFAULT_AUDIO_TYPE;
-      AUBIO_SPRINTF (name, "in_%d", i + 1);
+      AUBIO_SNPRINTF (name, sizeof (name), "in_%d", i + 1);
     } else {
       jack_port_type = JACK_DEFAULT_MIDI_TYPE;
-      AUBIO_SPRINTF (name, "midi_in_%d", i - ichan + 1);
+      AUBIO_SNPRINTF (name, sizeof (name), "midi_in_%d", i - ichan + 1);
     }
     if ((jack_setup->iports[i] =
             jack_port_register (jack_setup->client, name,
index f8c33d2..2336f45 100644 (file)
@@ -25,6 +25,7 @@
 #endif
 
 extern int verbose;
+extern int quiet;
 // input / output
 extern int usejack;
 extern char_t *source_uri;
@@ -127,6 +128,7 @@ void usage (FILE * stream, int exit_code)
       "       -V      --miditap-velo     MIDI velocity; default=65.\n"
 #endif /* defined(PROG_HAS_ONSET) && !defined(PROG_HAS_PITCH) */
 #endif /* defined(PROG_HAS_JACK) && defined(HAVE_JACK) */
+      "       -q      --quiet            be quiet\n"
       "       -v      --verbose          be verbose\n"
       "       -h      --help             display this message\n"
       );
@@ -141,7 +143,7 @@ int
 parse_args (int argc, char **argv)
 {
 #ifdef HAVE_GETOPT_H
-  const char *options = "hv"
+  const char *options = "hvq"
     "i:r:B:H:"
 #ifdef PROG_HAS_JACK
     "j"
@@ -173,6 +175,7 @@ parse_args (int argc, char **argv)
   struct option long_options[] = {
     {"help",                  0, NULL, 'h'},
     {"verbose",               0, NULL, 'v'},
+    {"quiet",                 0, NULL, 'q'},
     {"input",                 1, NULL, 'i'},
     {"samplerate",            1, NULL, 'r'},
     {"bufsize",               1, NULL, 'B'},
@@ -226,6 +229,9 @@ parse_args (int argc, char **argv)
       case 'v':                /* verbose */
         verbose = 1;
         break;
+      case 'q':                /* quiet */
+        quiet = 1;
+        break;
       case 'j':
         usejack = 1;
         break;
index c8e44a3..01c308d 100644 (file)
@@ -32,6 +32,7 @@
 #endif /* HAVE_JACK */
 
 int verbose = 0;
+int quiet = 0;
 int usejack = 0;
 // input / output
 char_t *sink_uri = NULL;
@@ -169,7 +170,7 @@ void examples_common_process (aubio_process_func_t process_func,
       aubio_source_do (this_source, input_buffer, &read);
       process_func (input_buffer, output_buffer);
       // print to console if verbose or no output given
-      if (verbose || sink_uri == NULL) {
+      if ((verbose || sink_uri == NULL) && !quiet) {
         print();
       }
       if (this_sink) {
index 555a30e..b85d2a0 100755 (executable)
@@ -97,7 +97,7 @@ ax3.plot(times, confidences)
 # draw a line at tolerance
 ax3.plot(times, [tolerance]*len(confidences))
 ax3.axis( xmin = times[0], xmax = times[-1])
-ax3.set_ylabel('condidence')
+ax3.set_ylabel('confidence')
 set_xlabels_sample2time(ax3, times[-1], samplerate)
 plt.show()
 #plt.savefig(os.path.basename(filename) + '.svg')
diff --git a/python/demos/demo_pitchshift.py b/python/demos/demo_pitchshift.py
new file mode 100755 (executable)
index 0000000..ad78794
--- /dev/null
@@ -0,0 +1,52 @@
+#! /usr/bin/env python
+
+import sys
+import aubio
+
+if __name__ == '__main__':
+    if len(sys.argv) < 3:
+        print('usage: %s <inputfile> <outputfile> [transpose] [samplerate] [hop_size] [mode]' % sys.argv[0])
+        print('available modes: default, crispness:0, crispness:1, ... crispness:6')
+        sys.exit(1)
+    if len(sys.argv) > 3: transpose = float(sys.argv[3])
+    else: transpose = 12.
+    if len(sys.argv) > 4: samplerate = int(sys.argv[4])
+    else: samplerate = 0
+    if len(sys.argv) > 5: hop_size = int(sys.argv[5])
+    else: hop_size = 64
+    if len(sys.argv) > 6: mode = sys.argv[6]
+    else: mode = "default"
+
+    source_read = aubio.source(sys.argv[1], samplerate, hop_size)
+    if samplerate == 0: samplerate = source_read.samplerate
+    sink_out = aubio.sink(sys.argv[2], samplerate)
+
+    pitchshifter = aubio.pitchshift(mode, 1., hop_size, samplerate)
+    if transpose: pitchshifter.set_transpose(transpose)
+
+    total_frames, read = 0, hop_size
+    transpose_range = 23.9
+    while read:
+        vec, read = source_read()
+        # transpose the samples
+        out = pitchshifter(vec)
+        # position in the file (between 0. and 1.)
+        percent_read = total_frames / float(source_read.duration)
+        # variable transpose rate (in semitones)
+        transpose = 2 * transpose_range * percent_read - transpose_range
+        # set transpose rate
+        pitchshifter.set_transpose(transpose)
+        # print the transposition
+        #print pitchshifter.get_transpose()
+        # write the output
+        sink_out(out, read)
+        total_frames += read
+
+    # end of file, print some results
+    outstr = "wrote %.2fs" % (total_frames / float(samplerate))
+    outstr += " (%d frames in" % total_frames
+    outstr += " %d blocks" % (total_frames // source_read.hop_size)
+    outstr += " at %dHz)" % source_read.samplerate
+    outstr += " from " + source_read.uri
+    outstr += " to " + sink_out.uri
+    print(outstr)
index d373d72..e564134 100644 (file)
@@ -3,8 +3,8 @@
 
 typedef smpl_t (*aubio_unary_func_t)(smpl_t input);
 
-static void aubio_PyUFunc_d_d(char **args, npy_intp *dimensions,
-                            npy_intp* steps, void* data)
+static void aubio_PyUFunc_d_d(char **args, const npy_intp *dimensions,
+                            const npy_intp* steps, void* data)
 {
     npy_intp i;
     npy_intp n = dimensions[0];
@@ -22,8 +22,8 @@ static void aubio_PyUFunc_d_d(char **args, npy_intp *dimensions,
     }
 }
 
-static void aubio_PyUFunc_f_f_As_d_d(char **args, npy_intp *dimensions,
-                            npy_intp* steps, void* data)
+static void aubio_PyUFunc_f_f_As_d_d(char **args, const npy_intp *dimensions,
+                            const npy_intp* steps, void* data)
 {
     npy_intp i;
     npy_intp n = dimensions[0];
index 3db270b..be7fec6 100644 (file)
@@ -20,6 +20,7 @@ aubiodefvalue = {
     'ratio': '0.5',
     'method': '"default"',
     'uri': '"none"',
+    'transpose': '0.',
     }
 
 member_types = {
@@ -83,6 +84,7 @@ objoutsize = {
         'tempo': '1',
         'filterbank': 'self->n_filters',
         'tss': 'self->buf_size',
+        'pitchshift': 'self->hop_size',
         'dct': 'self->size',
         }
 
@@ -96,6 +98,7 @@ objinputsize = {
         'tempo': 'self->hop_size',
         'wavetable': 'self->hop_size',
         'tss': 'self->buf_size / 2 + 1',
+        'pitchshift': 'self->hop_size',
         }
 
 def get_name(proto):
@@ -290,6 +293,8 @@ Py_{shortname}_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
             return self.check_valid_uint(p)
         if p['type'] == 'char_t*':
             return self.check_valid_char(p)
+        if p['type'] == 'smpl_t':
+            return self.check_valid_smpl(p)
         else:
             print ("ERROR, no idea how to check %s for validity" % p['type'])
 
@@ -314,6 +319,15 @@ Py_{shortname}_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
     }}
 """.format(defval = aubiodefvalue[name], name = name)
 
+    def check_valid_smpl(self, p):
+        name = p['name']
+        return """
+    self->{name} = {defval};
+    if ({name} != 0.) {{
+        self->{name} = {name};
+    }}
+""".format(defval = aubiodefvalue[name], name = name)
+
     def gen_init(self):
         out = """
 // init {shortname}
index e5f0a02..4ef7527 100644 (file)
@@ -46,6 +46,7 @@ default_skip_objects = [
     #'sampler',
     'audio_unit',
     'spectral_whitening',
+    'timestretch', # TODO fix parsing of uint_t *read in _do
 ]
 
 
index 299d1f9..0c70431 100644 (file)
@@ -76,9 +76,10 @@ def add_local_macros(ext, usedouble = False):
 def add_external_deps(ext, usedouble = False):
     # loof for additional packages
     print("Info: looking for *optional* additional packages")
-    packages = ['libavcodec', 'libavformat', 'libavutil',
-                'libswresample', 'libavresample',
+    packages = ['libavcodec', 'libavformat', 'libavutil', 'libswresample',
+                'jack',
                 'sndfile',
+                'rubberband',
                 #'fftw3f',
                ]
     # samplerate only works with float
@@ -89,17 +90,15 @@ def add_external_deps(ext, usedouble = False):
     add_packages(packages, ext=ext)
     if 'avcodec' in ext.libraries \
             and 'avformat' in ext.libraries \
-            and 'avutil' in ext.libraries:
-        if 'swresample' in ext.libraries:
-            ext.define_macros += [('HAVE_SWRESAMPLE', 1)]
-        elif 'avresample' in ext.libraries:
-            ext.define_macros += [('HAVE_AVRESAMPLE', 1)]
-        if 'swresample' in ext.libraries or 'avresample' in ext.libraries:
-            ext.define_macros += [('HAVE_LIBAV', 1)]
+            and 'avutil' in ext.libraries \
+            and 'swresample' in ext.libraries:
+        ext.define_macros += [('HAVE_LIBAV', 1)]
     if 'sndfile' in ext.libraries:
         ext.define_macros += [('HAVE_SNDFILE', 1)]
     if 'samplerate' in ext.libraries:
         ext.define_macros += [('HAVE_SAMPLERATE', 1)]
+    if 'rubberband' in ext.libraries:
+        ext.define_macros += [('HAVE_RUBBERBAND', 1)]
     if 'fftw3f' in ext.libraries:
         ext.define_macros += [('HAVE_FFTW3F', 1)]
         ext.define_macros += [('HAVE_FFTW3', 1)]
diff --git a/python/tests/test_pitchshift.py b/python/tests/test_pitchshift.py
new file mode 100755 (executable)
index 0000000..4d3dd3f
--- /dev/null
@@ -0,0 +1,104 @@
+#! /usr/bin/env python
+
+from numpy.testing import TestCase
+from _tools import parametrize, skipTest
+import numpy as np
+import aubio
+
+class aubio_pitchshift(TestCase):
+
+    def setUp(self):
+        try:
+            self.o = aubio.pitchshift(hop_size = 128)
+        except RuntimeError as e:
+            self.skipTest("creating aubio.pitchshift {}".format(e))
+
+    def test_default_creation(self):
+        self.assertEqual(self.o.get_pitchscale(), 1)
+        self.assertEqual(self.o.get_transpose(), 0)
+
+    def test_on_zeros(self):
+        test_length = self.o.hop_size * 100
+        read = 0
+        # test on zeros
+        vec = aubio.fvec(self.o.hop_size)
+        transpose_range = 24
+        while read < test_length:
+            # transpose the samples
+            out = self.o(vec)
+            self.assertTrue((out == 0).all())
+            # position in the file (between 0. and 1.)
+            percent_read = read / float(test_length)
+            # variable transpose rate (in semitones)
+            transpose = 2 * transpose_range * percent_read - transpose_range
+            # set transpose rate
+            self.o.set_transpose(transpose)
+            read += len(vec)
+
+    def test_on_ones(self):
+        test_length = self.o.hop_size * 100
+        read = 0
+        # test on zeros
+        vec = aubio.fvec(self.o.hop_size) + 1
+        transpose_range = 1.24
+        while read < test_length:
+            # transpose the samples
+            out = self.o(vec)
+            # position in the file (between 0. and 1.)
+            percent_read = read / float(test_length)
+            # variable transpose rate (in semitones)
+            transpose = 2 * transpose_range * percent_read - transpose_range
+            # set transpose rate
+            self.o.set_transpose(transpose)
+            read += len(vec)
+
+    def test_transpose_too_high(self):
+        with self.assertRaises(ValueError):
+            self.o.set_transpose(24.3)
+
+    def test_transpose_too_low(self):
+        with self.assertRaises(ValueError):
+            self.o.set_transpose(-24.3)
+
+class aubio_pitchshift_wrong_params(TestCase):
+
+    def test_wrong_transpose(self):
+        with self.assertRaises(RuntimeError):
+            aubio.pitchshift("default", -123)
+
+class Test_aubio_pitchshift_testruns(object):
+
+    run_args = ['mode', 'pitchscale', 'hop_size', 'samplerate']
+    run_values = [
+            ("default",     1.2,  128,  44100),
+            ("crispness:0", 0.43,  64,   8000),
+            ("crispness:3", 0.53, 256,   8000),
+            ("crispness:3", 1.53, 512,   8000),
+            ("crispness:6", 2.3, 4096, 192000),
+            ]
+
+    @parametrize(run_args, run_values)
+    def test_run_with_params(self, mode, pitchscale, hop_size, samplerate):
+        try:
+            self.o = aubio.pitchshift(mode, pitchscale, hop_size, samplerate)
+        except RuntimeError as e:
+            skipTest("failed creating pitchshift ({})".format(e))
+        test_length = self.o.hop_size * 50
+        read = 0
+        # test on random
+        vec = np.random.rand(self.o.hop_size).astype(aubio.float_type)
+        transpose_range = self.o.get_transpose()
+        while read < test_length:
+            # transpose the samples
+            out = self.o(vec)
+            # position in the file (between 0. and 1.)
+            percent_read = read / float(test_length)
+            # variable transpose rate (in semitones)
+            transpose =  transpose_range - 2 * transpose_range * percent_read
+            # set transpose rate
+            self.o.set_transpose(transpose)
+            read += len(vec)
+
+if __name__ == '__main__':
+    from _tools import run_module_suite
+    run_module_suite()
index e1e790c..37cfd35 100755 (executable)
@@ -1,4 +1,4 @@
-#! /bin/bash
+#! /usr/bin/env bash
 
 set -e
 set -x
index 9cb6661..17c8bc8 100755 (executable)
@@ -14,7 +14,7 @@ mkdir -p "$OUTPUTDIR"
 #VERSION+=+$(git log --pretty=format:"%h" -1)
 
 CFLAGS="-Werror -Os"
-WAFCONF="--disable-sndfile --disable-avcodec --disable-samplerate --enable-fat" # --disable-memcpy --disable-accelerate"
+WAFCONF="--nodeps --enable-fat" # --disable-memcpy --disable-accelerate"
 
 export VERSION
 
@@ -45,19 +45,6 @@ function create_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
@@ -72,7 +59,7 @@ set -e
 
 #./waf dist --verbose
 
-for PLATFORM in darwin ios iosimulator
+for PLATFORM in darwin ios iosimulator watchos watchsimulator
 do
   rm -rf $AUBIO_TMPDIR/dist-$PLATFORM
   WAF_OPTIONS="--verbose --destdir $AUBIO_TMPDIR/dist-$PLATFORM --with-target-platform $PLATFORM $WAFCONF"
@@ -84,19 +71,18 @@ do
   create_framework $PACKAGE $VERSION $PLATFORM
   if [ $PLATFORM == 'darwin' ]
   then
-    # on darwin, build a .tar.bz2 of /usr and a .zip of aubio.framework
+    # on darwin, build a .tar.bz2 of /usr
     create_tarballs $PACKAGE $VERSION $PLATFORM
-    create_framework_zip $PACKAGE $VERSION $PLATFORM
   fi
+
+  # build a .zip of aubio.framework
+  create_framework_zip $PACKAGE $VERSION $PLATFORM
+
+  # clean up
   ./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
 
index dd24cbc..87d2f64 100755 (executable)
@@ -1,4 +1,4 @@
-#! /bin/bash
+#! /usr/bin/env bash
 
 function checkprog() {
   type $1 >/dev/null 2>&1 || { echo >&2 "$1 required but not found, aborting."; exit 1; }
index b0a0d5a..f68a0ef 100755 (executable)
@@ -1,4 +1,4 @@
-#! /bin/bash
+#! /usr/bin/env bash
 
 # This script cross compiles aubio for windows using mingw, four times:
 #
index 5fdcea6..535b7c8 100755 (executable)
@@ -1,12 +1,12 @@
-#! /bin/bash
+#! /usr/bin/env bash
 
 set -e
 #set -x
 
-WAFVERSION=2.0.17
+WAFVERSION=2.0.26
 WAFTARBALL=waf-$WAFVERSION.tar.bz2
 WAFURL=https://waf.io/$WAFTARBALL
-WAFUPSTREAMKEY=https://gitlab.com/ita1024/waf/raw/master/utils/pubkey.asc
+WAFUPSTREAMKEY=https://gitlab.com/ita1024/waf/-/raw/waf-$WAFVERSION/utils/pubkey.asc
 
 WAFBUILDDIR=`mktemp -d`
 
@@ -48,7 +48,7 @@ fetchwaf
 buildwaf
 popd
 
-cp -prv $WAFBUILDDIR/waf-$WAFVERSION/waf $PWD
+cp -prv $WAFBUILDDIR/waf-$WAFVERSION/waf "$PWD"
 chmod +x waf
 
 cleanup
index 73deb24..3c9216d 100644 (file)
@@ -220,6 +220,8 @@ extern "C"
 #include "pitch/pitchfcomb.h"
 #include "pitch/pitchspecacf.h"
 #include "tempo/beattracking.h"
+#include "effects/pitchshift.h"
+#include "effects/timestretch.h"
 #include "utils/scale.h"
 #include "utils/hist.h"
 #endif
index 8cb6756..df56ca5 100644 (file)
@@ -260,7 +260,7 @@ uint_t aubio_log(sint_t level, const char_t *fmt, ...);
 #define AUBIO_ERROR   AUBIO_ERR
 
 #define AUBIO_QUIT(_s)               exit(_s)
-#define AUBIO_SPRINTF                sprintf
+#define AUBIO_SNPRINTF               snprintf
 
 #define AUBIO_MAX_SAMPLERATE (192000*8)
 #define AUBIO_MAX_CHANNELS 1024
@@ -341,7 +341,7 @@ uint_t aubio_log(sint_t level, const char_t *fmt, ...);
 #define isnan _isnan
 #endif
 
-#if !defined(_MSC_VER)
+#if !defined(_WIN32)
 #define AUBIO_STRERROR(errno,buf,len) strerror_r(errno, buf, len)
 #else
 #define AUBIO_STRERROR(errno,buf,len) strerror_s(buf, len, errno)
diff --git a/src/effects/pitchshift.h b/src/effects/pitchshift.h
new file mode 100644 (file)
index 0000000..808ac60
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+  Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef AUBIO_PITCHSHIFT_H
+#define AUBIO_PITCHSHIFT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+
+  Pitch shifting object
+
+  ::aubio_pitchshift_t can be used to transpose a stream of blocks of frames.
+
+  \example effects/test-pitchshift.c
+
+*/
+
+/** pitch shifting object */
+typedef struct _aubio_pitchshift_t aubio_pitchshift_t;
+
+/** execute pitch shifting on an input signal frame
+
+  \param o pitch shifting object as returned by new_aubio_pitchshift()
+  \param in input signal of size [hop_size]
+  \param out output pitch candidates of size [1]
+
+*/
+void aubio_pitchshift_do (aubio_pitchshift_t * o, const fvec_t * in,
+        fvec_t * out);
+
+/** deletion of the pitch shifting object
+
+  \param o pitch shifting object as returned by new_aubio_pitchshift()
+
+*/
+void del_aubio_pitchshift (aubio_pitchshift_t * o);
+
+/** creation of the pitch shifting object
+
+  \param method set pitch shifting algorithm ("default")
+  \param transpose initial pitch transposition
+  \param hop_size step size between two consecutive analysis instant
+  \param samplerate sampling rate of the signal
+
+  \return newly created ::aubio_pitchshift_t
+
+*/
+aubio_pitchshift_t *new_aubio_pitchshift (const char_t * method,
+    smpl_t transpose, uint_t hop_size, uint_t samplerate);
+
+/** get the latency of the pitch shifting object, in samples
+
+  \param o pitch shifting object as returned by ::new_aubio_pitchshift()
+
+  \return latency of the pitch shifting object in samples
+
+*/
+uint_t aubio_pitchshift_get_latency (aubio_pitchshift_t * o);
+
+/** set the pitch scale of the pitch shifting object
+
+  \param o pitch shifting object as returned by new_aubio_pitchshift()
+  \param pitchscale new pitch scale of the pitch shifting object
+
+  pitchscale is a frequency ratio. It should be in the range [0.25, 4].
+
+  \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * o,
+        smpl_t pitchscale);
+
+/** get the pitchscale of the pitch shifting object
+
+  \param o pitch shifting object as returned by ::new_aubio_pitchshift()
+
+  \return pitchscale of the pitch shifting object
+
+*/
+smpl_t aubio_pitchshift_get_pitchscale (aubio_pitchshift_t * o);
+
+/** set the transposition of the pitch shifting object, in semitones
+
+  \param o pitch shifting object as returned by new_aubio_pitchshift()
+  \param transpose new pitch transposition of the pitch shifting object,
+  expressed in semitones (should be in the range [-24;+24])
+
+  \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_pitchshift_set_transpose (aubio_pitchshift_t * o,
+        smpl_t transpose);
+
+/** get the transposition of the pitch shifting object, in semitones
+
+  \param o pitch shifting object as returned by ::new_aubio_pitchshift()
+
+  \return transposition of the pitch shifting object, in semitones
+
+*/
+smpl_t aubio_pitchshift_get_transpose (aubio_pitchshift_t * o);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUBIO_PITCHSHIFT_H */
diff --git a/src/effects/pitchshift_dummy.c b/src/effects/pitchshift_dummy.c
new file mode 100644 (file)
index 0000000..ed16ca0
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifndef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "effects/pitchshift.h"
+
+// TODO fallback pitch shifting implementation
+
+struct _aubio_pitchshift_t
+{
+  void *dummy;
+};
+
+void aubio_pitchshift_do (aubio_pitchshift_t * o UNUSED, const fvec_t * in UNUSED,
+    fvec_t * out UNUSED) {
+}
+
+void del_aubio_pitchshift (aubio_pitchshift_t * o UNUSED) {
+}
+
+aubio_pitchshift_t *new_aubio_pitchshift (const char_t * method UNUSED,
+    smpl_t pitchscale UNUSED, uint_t hop_size UNUSED, uint_t samplerate UNUSED)
+{
+  AUBIO_ERR ("aubio was not compiled with rubberband\n");
+  return NULL;
+}
+
+uint_t aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * o UNUSED, smpl_t pitchscale UNUSED)
+{
+  return AUBIO_FAIL;
+}
+
+smpl_t aubio_pitchshift_get_pitchscale (aubio_pitchshift_t * o UNUSED)
+{
+  return 1.;
+}
+
+uint_t aubio_pitchshift_set_transpose (aubio_pitchshift_t * o UNUSED, smpl_t transpose UNUSED) {
+  return AUBIO_FAIL;
+}
+
+smpl_t aubio_pitchshift_get_transpose (aubio_pitchshift_t * o UNUSED) {
+  return 0.;
+}
+
+uint_t aubio_pitchshift_get_latency (aubio_pitchshift_t * o UNUSED) {
+  return 0.;
+}
+
+// end of dummy implementation
+
+#endif /* HAVE_RUBBERBAND */
diff --git a/src/effects/pitchshift_rubberband.c b/src/effects/pitchshift_rubberband.c
new file mode 100644 (file)
index 0000000..1211dca
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+  Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "effects/pitchshift.h"
+
+#include <rubberband/rubberband-c.h>
+
+/** generic pitch shifting structure */
+struct _aubio_pitchshift_t
+{
+  uint_t samplerate;              /**< samplerate */
+  uint_t hopsize;                 /**< hop size */
+  smpl_t pitchscale;              /**< pitch scale */
+
+  RubberBandState rb;
+  RubberBandOptions rboptions;
+};
+
+extern RubberBandOptions aubio_get_rubberband_opts(const char_t *mode);
+
+aubio_pitchshift_t *
+new_aubio_pitchshift (const char_t * mode,
+    smpl_t transpose, uint_t hopsize, uint_t samplerate)
+{
+  aubio_pitchshift_t *p = AUBIO_NEW (aubio_pitchshift_t);
+  p->samplerate = samplerate;
+  p->hopsize = hopsize;
+  p->pitchscale = 1.;
+  p->rb = NULL;
+  if ((sint_t)hopsize <= 0) {
+    AUBIO_ERR("pitchshift: hop_size should be >= 0, got %d\n", hopsize);
+    goto beach;
+  }
+  if ((sint_t)samplerate <= 0) {
+    AUBIO_ERR("pitchshift: samplerate should be >= 0, got %d\n", samplerate);
+    goto beach;
+  }
+
+  p->rboptions = aubio_get_rubberband_opts(mode);
+  if (p->rboptions < 0) {
+    AUBIO_ERR("pitchshift: unknown pitch shifting method %s\n", mode);
+    goto beach;
+  }
+
+  //AUBIO_MSG("pitchshift: using pitch shifting method %s\n", mode);
+
+  p->rb = rubberband_new(samplerate, 1, p->rboptions, 1., p->pitchscale);
+  rubberband_set_max_process_size(p->rb, p->hopsize);
+  //rubberband_set_debug_level(p->rb, 10);
+
+  if (aubio_pitchshift_set_transpose(p, transpose)) goto beach;
+
+#if 1
+  // warm up rubber band
+  unsigned int latency = MAX(p->hopsize, rubberband_get_latency(p->rb));
+  int available = rubberband_available(p->rb);
+  fvec_t *zeros = new_fvec(p->hopsize);
+  while (available <= (int)latency) {
+    rubberband_process(p->rb,
+        (const float* const*)&(zeros->data), p->hopsize, 0);
+    available = rubberband_available(p->rb);
+  }
+  del_fvec(zeros);
+#endif
+
+  return p;
+
+beach:
+  del_aubio_pitchshift(p);
+  return NULL;
+}
+
+void
+del_aubio_pitchshift (aubio_pitchshift_t * p)
+{
+  if (p->rb) {
+    rubberband_delete(p->rb);
+  }
+  AUBIO_FREE (p);
+}
+
+uint_t aubio_pitchshift_get_latency (aubio_pitchshift_t * p) {
+  return rubberband_get_latency(p->rb);
+}
+
+uint_t
+aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * p, smpl_t pitchscale)
+{
+  if (pitchscale >= 0.25  && pitchscale <= 4.) {
+    p->pitchscale = pitchscale;
+    rubberband_set_pitch_scale(p->rb, p->pitchscale);
+    return AUBIO_OK;
+  } else {
+    AUBIO_ERR("pitchshift: could not set pitchscale to '%f',"
+        " should be in the range [0.25, 4.].\n", pitchscale);
+    return AUBIO_FAIL;
+  }
+}
+
+smpl_t
+aubio_pitchshift_get_pitchscale (aubio_pitchshift_t * p)
+{
+  return p->pitchscale;
+}
+
+uint_t
+aubio_pitchshift_set_transpose(aubio_pitchshift_t * p, smpl_t transpose)
+{
+  if (transpose >= -24. && transpose <= 24.) {
+    smpl_t pitchscale = POW(2., transpose / 12.);
+    return aubio_pitchshift_set_pitchscale(p, pitchscale);
+  } else {
+    AUBIO_ERR("pitchshift: could not set transpose to '%f',"
+        " should be in the range [-24; 24.].\n", transpose);
+    return AUBIO_FAIL;
+  }
+}
+
+smpl_t
+aubio_pitchshift_get_transpose(aubio_pitchshift_t * p)
+{
+  return 12. * LOG(p->pitchscale) / LOG(2.0);
+}
+
+void
+aubio_pitchshift_do (aubio_pitchshift_t * p, const fvec_t * in, fvec_t * out)
+{
+  // third parameter is always 0 since we are never expecting a final frame
+  rubberband_process(p->rb, (const float* const*)&(in->data), p->hopsize, 0);
+  if (rubberband_available(p->rb) >= (int)p->hopsize) {
+    rubberband_retrieve(p->rb, (float* const*)&(out->data), p->hopsize);
+  } else {
+    AUBIO_WRN("pitchshift: catching up with zeros"
+        ", only %d available, needed: %d, current pitchscale: %f\n",
+        rubberband_available(p->rb), p->hopsize, p->pitchscale);
+    fvec_zeros(out);
+  }
+}
+
+#endif
diff --git a/src/effects/rubberband_utils.c b/src/effects/rubberband_utils.c
new file mode 100644 (file)
index 0000000..6e904c4
--- /dev/null
@@ -0,0 +1,150 @@
+
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_RUBBERBAND
+
+#include <rubberband/rubberband-c.h>
+
+// check rubberband is 1.8.1, warn if 1.3
+#if !((RUBBERBAND_API_MAJOR_VERSION >= 2) && \
+    (RUBBERBAND_API_MINOR_VERSION >= 5))
+#warning RubberBandOptionDetectorSoft not available, \
+ please upgrade rubberband to version 1.8.1 or higher
+#define RubberBandOptionDetectorSoft 0x00000000
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char_t** aubio_split_str(const char_t* str, const char_t sep) {
+  char_t** result = 0;
+  uint_t count = 0;
+  char_t input[PATH_MAX];
+  char_t* in_ptr = input;
+  char_t* last_sep = 0;
+  char_t delim[2]; delim[0] = sep; delim[1] = 0;
+
+  strncpy(input, str, PATH_MAX);
+  input[PATH_MAX - 1] = '\0';
+
+  // count number of elements
+  while (*in_ptr) {
+    if (sep == *in_ptr) {
+      count++;
+      last_sep = in_ptr;
+    }
+    in_ptr++;
+  }
+  // add space for trailing token.
+  count += last_sep < (input + strlen(input) - 1);
+  count++;
+
+  result = AUBIO_ARRAY(char_t*, count);
+  if (result) {
+    uint_t idx = 0;
+    char_t* params = strtok(input, delim);
+    while (params) {
+      // make sure we don't got in the wild
+      if (idx >= count)
+        break;
+      *(result + idx++) = strdup(params);
+      params = strtok(0, delim);
+    }
+    // add null string at the end if needed
+    if (idx < count - 1)
+      *(result + idx) = 0;
+  }
+  return result;
+}
+
+RubberBandOptions aubio_get_rubberband_opts(const char_t *mode)
+{
+  RubberBandOptions rboptions = RubberBandOptionProcessRealTime;
+
+  if ( strcmp(mode,"crispness:0") == 0 ) {
+    rboptions |= RubberBandOptionTransientsSmooth;
+    rboptions |= RubberBandOptionWindowLong;
+    rboptions |= RubberBandOptionPhaseIndependent;
+  } else if ( strcmp(mode, "crispness:1") == 0 ) {
+    rboptions |= RubberBandOptionDetectorSoft;
+    rboptions |= RubberBandOptionTransientsSmooth;
+    rboptions |= RubberBandOptionWindowLong;
+    rboptions |= RubberBandOptionPhaseIndependent;
+  } else if ( strcmp(mode, "crispness:2") == 0 ) {
+    rboptions |= RubberBandOptionTransientsSmooth;
+    rboptions |= RubberBandOptionPhaseIndependent;
+  } else if ( strcmp(mode, "crispness:3") == 0 ) {
+    rboptions |= RubberBandOptionTransientsSmooth;
+  } else if ( strcmp(mode, "crispness:4") == 0 ) {
+    // same as "default"
+  } else if ( strcmp(mode, "crispness:5") == 0 ) {
+    rboptions |= RubberBandOptionTransientsCrisp;
+  } else if ( strcmp(mode, "crispness:6") == 0 ) {
+    rboptions |= RubberBandOptionTransientsCrisp;
+    rboptions |= RubberBandOptionWindowShort;
+    rboptions |= RubberBandOptionPhaseIndependent;
+  } else if ( strcmp(mode, "default") == 0 ) {
+    // nothing to do
+  } else {
+    // attempt to parse a list of options, separated with ','
+    char_t **params = aubio_split_str(mode, ':');
+    uint_t i = 0;
+    if (!params || !params[0]) {
+      // memory failure occurred or empty string was passed
+      AUBIO_ERR("rubberband_utils: failed parsing options\n");
+      rboptions = -1;
+    }
+    while (*(params + i) != NULL) {
+      if ( strcmp(params[i], "ProcessOffline" ) == 0 )        {
+             rboptions = RubberBandOptionProcessOffline;
+        // TODO: add wrapper to rb study(smpl_t *input, uint_t write)
+        AUBIO_ERR("rubberband_utils: RubberBandOptionProcessOffline is not available\n");
+        rboptions = -1;
+      }
+      else if ( strcmp(params[i], "ProcessRealTime" ) == 0 )       rboptions |= RubberBandOptionProcessRealTime;
+      else if ( strcmp(params[i], "StretchElastic" ) == 0 )        rboptions |= RubberBandOptionStretchElastic;
+      else if ( strcmp(params[i], "StretchPrecise" ) == 0 )        rboptions |= RubberBandOptionStretchPrecise;
+      else if ( strcmp(params[i], "TransientsCrisp" ) == 0 )       rboptions |= RubberBandOptionTransientsCrisp;
+      else if ( strcmp(params[i], "TransientsMixed" ) == 0 )       rboptions |= RubberBandOptionTransientsMixed;
+      else if ( strcmp(params[i], "TransientsSmooth" ) == 0 )      rboptions |= RubberBandOptionTransientsSmooth;
+      else if ( strcmp(params[i], "DetectorCompound" ) == 0 )      rboptions |= RubberBandOptionDetectorCompound;
+      else if ( strcmp(params[i], "DetectorPercussive" ) == 0 )    rboptions |= RubberBandOptionDetectorPercussive;
+      else if ( strcmp(params[i], "DetectorSoft" ) == 0 )          rboptions |= RubberBandOptionDetectorSoft;
+      else if ( strcmp(params[i], "PhaseLaminar" ) == 0 )          rboptions |= RubberBandOptionPhaseLaminar;
+      else if ( strcmp(params[i], "PhaseIndependent" ) == 0 )      rboptions |= RubberBandOptionPhaseIndependent;
+      else if ( strcmp(params[i], "ThreadingAuto" ) == 0 )         rboptions |= RubberBandOptionThreadingAuto;
+      else if ( strcmp(params[i], "ThreadingNever" ) == 0 )        rboptions |= RubberBandOptionThreadingNever;
+      else if ( strcmp(params[i], "ThreadingAlways" ) == 0 )       rboptions |= RubberBandOptionThreadingAlways;
+      else if ( strcmp(params[i], "WindowStandard" ) == 0 )        rboptions |= RubberBandOptionWindowStandard;
+      else if ( strcmp(params[i], "WindowShort" ) == 0 )           rboptions |= RubberBandOptionWindowShort;
+      else if ( strcmp(params[i], "WindowLong" ) == 0 )            rboptions |= RubberBandOptionWindowLong;
+      else if ( strcmp(params[i], "SmoothingOff" ) == 0 )          rboptions |= RubberBandOptionSmoothingOff;
+      else if ( strcmp(params[i], "SmoothingOn" ) == 0 )           rboptions |= RubberBandOptionSmoothingOn;
+      else if ( strcmp(params[i], "FormantShifted" ) == 0 )        rboptions |= RubberBandOptionFormantShifted;
+      else if ( strcmp(params[i], "FormantPreserved" ) == 0 )      rboptions |= RubberBandOptionFormantPreserved;
+      else if ( strcmp(params[i], "PitchHighSpeed" ) == 0 )        rboptions |= RubberBandOptionPitchHighSpeed;
+      else if ( strcmp(params[i], "PitchHighQuality" ) == 0 )      rboptions |= RubberBandOptionPitchHighQuality;
+      else if ( strcmp(params[i], "PitchHighConsistency" ) == 0 )  rboptions |= RubberBandOptionPitchHighConsistency;
+      else if ( strcmp(params[i], "ChannelsApart" ) == 0 )         rboptions |= RubberBandOptionChannelsApart;
+      else if ( strcmp(params[i], "ChannelsTogether" ) == 0 )      rboptions |= RubberBandOptionChannelsTogether;
+      else {
+        AUBIO_ERR("rubberband_utils: did not understand option '%s', should be one of: "
+          "StretchElastic|StretchPrecise, TransientsCrisp|TransientsMixed|TransientsSmooth, "
+          "DetectorCompound|DetectorPercussive|DetectorSoft, PhaseLaminar|PhaseIndependent, "
+          "ThreadingAuto|ThreadingNever|ThreadingAlways, WindowStandard|WindowLong|WindowShort, "
+          "SmoothingOn|SmoothingOff, FormantShifted|FormantPreserved, "
+          "PitchHighSpeed|PitchHighQuality|PitchHighConsistency, ChannelsApart|ChannelsTogether\n"
+          , params[i]);
+        rboptions = -1;
+      }
+      AUBIO_FREE(params[i]);
+      i++;
+    }
+    AUBIO_FREE(params);
+  }
+  return rboptions;
+}
+
+#endif
diff --git a/src/effects/timestretch.h b/src/effects/timestretch.h
new file mode 100644 (file)
index 0000000..b1454d0
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+  Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef AUBIO_TIMESTRETCH_H
+#define AUBIO_TIMESTRETCH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+
+  time stretching object
+
+  ::aubio_timestretch_t can be used to open a source file, read samples from
+  it, time-stretch them, and write out the modified samples.
+
+  The time-stretching factor can be changed at any time using
+  aubio_timestretch_set_stretch().
+
+  A transposition can also be applied and changed at any time with
+  aubio_timestretch_set_transpose().
+
+  \example effects/test-timestretch.c
+
+*/
+
+/** time stretching object */
+typedef struct _aubio_timestretch_t aubio_timestretch_t;
+
+/** execute time stretching on an input signal frame
+
+  \param o time stretching object as returned by new_aubio_timestretch()
+  \param out timestretched output of size [hop_size]
+  \param read number of frames actually wrote out
+
+*/
+void aubio_timestretch_do (aubio_timestretch_t * o, fvec_t * out,
+   uint_t * read);
+
+/** deletion of the time stretching object
+
+  \param o time stretching object as returned by new_aubio_timestretch()
+
+*/
+void del_aubio_timestretch (aubio_timestretch_t * o);
+
+/** creation of the time stretching object
+
+  \param method time stretching algorithm ("default")
+  \param stretch initial time stretching factor
+  \param hop_size block size at which the frames should be produced
+  \param samplerate sampling rate of the signal
+
+  \return newly created ::aubio_timestretch_t
+
+*/
+aubio_timestretch_t *new_aubio_timestretch (const char_t * method,
+    smpl_t stretch, uint_t hop_size, uint_t samplerate);
+
+/** push length samples from in to time stretching object
+
+  \param o time stretching object as returned by ::new_aubio_timestretch()
+  \param in input vector of new samples to push to time stretching object
+  \param length number of new samples to push from input vector
+
+  \return number of currently available samples
+
+ */
+sint_t aubio_timestretch_push(aubio_timestretch_t * o, fvec_t *in,
+    uint_t length);
+
+/** get number of currently available samples from time stretching object
+
+  \param o time stretching object as returned by ::new_aubio_timestretch()
+
+  \return number of currently available samples
+
+ */
+sint_t aubio_timestretch_get_available(aubio_timestretch_t * o);
+
+/** get the latency of the time stretching object, in samples
+
+  \param o time stretching object as returned by ::new_aubio_timestretch()
+
+  \return latency of the time stretching object in samples
+
+*/
+uint_t aubio_timestretch_get_latency (aubio_timestretch_t * o);
+
+/** get the samplerate of the time stretching object
+
+  Call after new_aubio_timestretch() was called with 0 to match the original
+  samplerate of the input file.
+
+  \param o time stretching object as returned by new_aubio_timestretch()
+
+  \return samplerate of the time stretching object
+
+ */
+uint_t aubio_timestretch_get_samplerate (aubio_timestretch_t * o);
+
+/** set the stretching ratio of the time stretching object
+
+  \param o time stretching object as returned by new_aubio_timestretch()
+  \param stretch new time stretching ratio of the time stretching object
+  (should be in the range [0.025; 10.])
+
+  \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_set_stretch (aubio_timestretch_t * o, smpl_t stretch);
+
+/** get the transposition of the time stretching object, in semitones
+
+  \param o time stretching object as returned by ::new_aubio_timestretch()
+
+  \return time stretching ratio of the time stretching object, in the range
+  [0.025; 10.]
+
+*/
+smpl_t aubio_timestretch_get_stretch (aubio_timestretch_t * o);
+
+/** set the pitch scale of the time stretching object
+
+  \param o time stretching object as returned by new_aubio_timestretch()
+  \param pitchscale new pitch scale of the time stretching object
+
+  pitchscale is a frequency ratio. It should be in the range [0.25, 4].
+
+  \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_set_pitchscale (aubio_timestretch_t * o,
+        smpl_t pitchscale);
+
+/** get the pitchscale of the time stretching object
+
+  \param o time stretching object as returned by ::new_aubio_timestretch()
+
+  \return pitchscale of the time stretching object
+
+*/
+smpl_t aubio_timestretch_get_pitchscale (aubio_timestretch_t * o);
+
+/** set the transposition of the time stretching object, in semitones
+
+  \param o time stretching object as returned by new_aubio_timestretch()
+
+  \param transpose new pitch transposition of the time stretching object,
+  expressed in semitones (should be in the range [-24;+24])
+
+  \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_set_transpose (aubio_timestretch_t * o,
+        smpl_t transpose);
+
+/** get the transposition of the time stretching object, in semitones
+
+  \param o time stretching object as returned by ::new_aubio_timestretch()
+
+  \return transposition of the time stretching object, in semitones
+
+*/
+smpl_t aubio_timestretch_get_transpose (aubio_timestretch_t * o);
+
+/** reset the time stretching object
+
+  \param o time stretching object as returned by ::new_aubio_timestretch()
+
+  \return 0 on success, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_reset(aubio_timestretch_t * o);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUBIO_TIMESTRETCH_H */
diff --git a/src/effects/timestretch_dummy.c b/src/effects/timestretch_dummy.c
new file mode 100644 (file)
index 0000000..adb8055
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+  Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifndef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "effects/timestretch.h"
+
+// TODO fallback time stretching implementation
+
+struct _aubio_timestretch_t
+{
+  void *dummy;
+};
+
+void
+aubio_timestretch_do (aubio_timestretch_t * o UNUSED, fvec_t * out UNUSED,
+    uint_t * read UNUSED)
+{
+}
+
+void del_aubio_timestretch (aubio_timestretch_t * o UNUSED) {
+}
+
+aubio_timestretch_t *
+new_aubio_timestretch (const char_t * method UNUSED,
+    smpl_t pitchscale UNUSED, uint_t hop_size UNUSED, uint_t samplerate UNUSED)
+{
+  AUBIO_ERR ("timestretch: aubio was not compiled with rubberband\n");
+  return NULL;
+}
+
+uint_t aubio_timestretch_set_stretch (aubio_timestretch_t * o UNUSED, smpl_t stretch UNUSED)
+{
+  return AUBIO_FAIL;
+}
+
+smpl_t aubio_timestretch_get_stretch (aubio_timestretch_t * o UNUSED)
+{
+  return 1.;
+}
+
+uint_t aubio_timestretch_set_pitchscale (aubio_timestretch_t * o UNUSED, smpl_t pitchscale UNUSED)
+{
+  return AUBIO_FAIL;
+}
+
+uint_t aubio_timestretch_get_samplerate (aubio_timestretch_t * o UNUSED) {
+  return 0;
+}
+
+smpl_t aubio_timestretch_get_pitchscale (aubio_timestretch_t * o UNUSED)
+{
+  return 1.;
+}
+
+uint_t aubio_timestretch_set_transpose (aubio_timestretch_t * o UNUSED, smpl_t transpose UNUSED) {
+  return AUBIO_FAIL;
+}
+
+smpl_t aubio_timestretch_get_transpose (aubio_timestretch_t * o UNUSED) {
+  return 0.;
+}
+
+uint_t aubio_timestretch_get_latency (aubio_timestretch_t * o UNUSED) {
+  return 0.;
+}
+
+uint_t aubio_timestretch_reset(aubio_timestretch_t *o UNUSED) {
+  return AUBIO_FAIL;
+}
+
+sint_t aubio_timestretch_push(aubio_timestretch_t * o UNUSED, fvec_t * in
+    UNUSED, uint_t length UNUSED) {
+  return AUBIO_FAIL;
+}
+
+sint_t aubio_timestretch_get_available(aubio_timestretch_t * o UNUSED) {
+  return AUBIO_FAIL;
+}
+// end of dummy implementation
+
+#endif /* HAVE_RUBBERBAND */
diff --git a/src/effects/timestretch_rubberband.c b/src/effects/timestretch_rubberband.c
new file mode 100644 (file)
index 0000000..d214490
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+  Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "fmat.h"
+#include "io/source.h"
+#include "effects/timestretch.h"
+
+#include <rubberband/rubberband-c.h>
+
+#define MIN_STRETCH_RATIO 0.025
+#define MAX_STRETCH_RATIO 40.
+
+#define HAVE_THREADS 1
+#if 0
+#undef HAVE_THREADS
+#endif
+
+#ifdef HAVE_THREADS
+#include <pthread.h>
+#endif
+
+/** generic time stretching structure */
+struct _aubio_timestretch_t
+{
+  uint_t samplerate;              /**< samplerate */
+  uint_t hopsize;                 /**< hop size */
+  smpl_t stretchratio;            /**< time ratio */
+  smpl_t pitchscale;              /**< pitch scale */
+
+  RubberBandState rb;
+  RubberBandOptions rboptions;
+};
+
+extern RubberBandOptions aubio_get_rubberband_opts(const char_t *mode);
+
+//static void aubio_timestretch_warmup (aubio_timestretch_t * p);
+
+aubio_timestretch_t *
+new_aubio_timestretch (const char_t * mode, smpl_t stretchratio, uint_t hopsize,
+    uint_t samplerate)
+{
+  aubio_timestretch_t *p = AUBIO_NEW (aubio_timestretch_t);
+  p->hopsize = hopsize;
+  p->pitchscale = 1.;
+
+  if ((sint_t)hopsize <= 0) {
+    AUBIO_ERR("timestretch: hopsize should be > 0, got %d\n", hopsize);
+    goto beach;
+  }
+
+  if ((sint_t)samplerate <= 0) {
+    AUBIO_ERR("timestretch: samplerate should be > 0, got %d\n", samplerate);
+    goto beach;
+  }
+
+  if (stretchratio <= MAX_STRETCH_RATIO && stretchratio >= MIN_STRETCH_RATIO) {
+    p->stretchratio = stretchratio;
+  } else {
+    AUBIO_ERR("timestretch: stretchratio should be in the range [%.3f, %.3f], got %f\n",
+        MIN_STRETCH_RATIO, MAX_STRETCH_RATIO, stretchratio);
+    goto beach;
+  }
+
+  p->rboptions = aubio_get_rubberband_opts(mode);
+  if (p->rboptions < 0) {
+    AUBIO_ERR("timestretch: unknown time stretching method %s\n", mode);
+    goto beach;
+  }
+
+  p->rb = rubberband_new(samplerate, 1, p->rboptions, p->stretchratio, p->pitchscale);
+  if (!p->rb) goto beach;
+
+  p->samplerate = samplerate;
+
+  //aubio_timestretch_warmup(p);
+
+  return p;
+
+beach:
+  del_aubio_timestretch(p);
+  return NULL;
+}
+
+#if 0
+static void
+aubio_timestretch_warmup (aubio_timestretch_t * p)
+{
+  // warm up rubber band
+  //AUBIO_WRN("timestretch: warming-up\n");
+  unsigned int latency = MAX(p->hopsize, rubberband_get_latency(p->rb));
+  fvec_t *input = new_fvec(p->hopsize);
+  while (aubio_timestretch_push(p, input, input->length) < (int)latency) {
+    //sint_t available = aubio_timestretch_get_available(p);
+    //AUBIO_WRN("timestretch: warmup got %d, latency: %d\n", available, latency);
+  }
+  del_fvec(input);
+}
+#endif
+
+void
+del_aubio_timestretch (aubio_timestretch_t * p)
+{
+  if (p->rb) {
+    rubberband_delete(p->rb);
+  }
+  AUBIO_FREE (p);
+}
+
+uint_t
+aubio_timestretch_get_samplerate (aubio_timestretch_t * p)
+{
+  return p->samplerate;
+}
+
+uint_t aubio_timestretch_get_latency (aubio_timestretch_t * p) {
+  return rubberband_get_latency(p->rb);
+}
+
+uint_t
+aubio_timestretch_set_stretch (aubio_timestretch_t * p, smpl_t stretch)
+{
+  if (!p->rb) {
+    AUBIO_ERR("timestretch: could not set stretch ratio,"
+       " rubberband not created\n");
+    return AUBIO_FAIL;
+  }
+  if (stretch >= MIN_STRETCH_RATIO && stretch <= MAX_STRETCH_RATIO) {
+    p->stretchratio = stretch;
+    rubberband_set_time_ratio(p->rb, 1./p->stretchratio);
+    return AUBIO_OK;
+  } else {
+    AUBIO_ERR("timestretch: could not set stretch ratio to '%f',"
+        " should be in the range [%.2f, %.2f].\n", stretch,
+        MIN_STRETCH_RATIO, MAX_STRETCH_RATIO);
+    return AUBIO_FAIL;
+  }
+}
+
+smpl_t
+aubio_timestretch_get_stretch (aubio_timestretch_t * p)
+{
+  return p->stretchratio;
+}
+
+uint_t
+aubio_timestretch_set_pitchscale (aubio_timestretch_t * p, smpl_t pitchscale)
+{
+  if (!p->rb) {
+    AUBIO_ERR("timestretch: could not set pitch scale,"
+       " rubberband not created\n");
+    return AUBIO_FAIL;
+  }
+  if (pitchscale >= 0.0625  && pitchscale <= 4.) {
+    p->pitchscale = pitchscale;
+    rubberband_set_pitch_scale(p->rb, p->pitchscale);
+    return AUBIO_OK;
+  } else {
+    AUBIO_ERR("timestretch: could not set pitchscale to '%f',"
+        " should be in the range [0.0625, 4.].\n", pitchscale);
+    return AUBIO_FAIL;
+  }
+}
+
+smpl_t
+aubio_timestretch_get_pitchscale (aubio_timestretch_t * p)
+{
+  return p->pitchscale;
+}
+
+uint_t
+aubio_timestretch_set_transpose(aubio_timestretch_t * p, smpl_t transpose)
+{
+  if (transpose >= -24. && transpose <= 24.) {
+    smpl_t pitchscale = POW(2., transpose / 12.);
+    return aubio_timestretch_set_pitchscale(p, pitchscale);
+  } else {
+    AUBIO_ERR("timestretch: could not set transpose to '%f',"
+        " should be in the range [-24; 24].\n", transpose);
+    return AUBIO_FAIL;
+  }
+}
+
+smpl_t
+aubio_timestretch_get_transpose(aubio_timestretch_t * p)
+{
+  return 12. * LOG(p->pitchscale) / LOG(2.0);
+}
+
+sint_t
+aubio_timestretch_push(aubio_timestretch_t *p, fvec_t *input, uint_t length)
+{
+  // push new samples to rubberband, return available
+  int available;
+  int eof = (input->length != length) ? 1 : 0;
+  rubberband_process(p->rb, (const float* const*)&(input->data), length, eof);
+  available = rubberband_available(p->rb);
+  //AUBIO_WRN("timestretch: processed %d, %d available, eof: %d\n",
+  //    length, available, eof);
+  return available;
+}
+
+sint_t
+aubio_timestretch_get_available(aubio_timestretch_t *p) {
+  return rubberband_available(p->rb);
+}
+
+void
+aubio_timestretch_do(aubio_timestretch_t * p, fvec_t * out, uint_t * read)
+{
+  // now retrieve the samples and write them into out->data
+  int available = rubberband_available(p->rb);
+  if (available >= (int)out->length) {
+    rubberband_retrieve(p->rb, (float* const*)&(out->data), out->length);
+    *read = out->length;
+  } else if (available > 0) {
+    // this occurs each time the end of file is reached
+    //AUBIO_WRN("timestretch: short read\n");
+    rubberband_retrieve(p->rb, (float* const*)&(out->data), available);
+    fvec_t zeros; zeros.length = out->length - available; zeros.data = out->data + available;
+    fvec_zeros(&zeros);
+    *read = available;
+  } else {
+    // this may occur if the previous was a short read available == hopsize
+    fvec_zeros(out);
+    *read = 0;
+  }
+}
+
+uint_t
+aubio_timestretch_reset(aubio_timestretch_t *p)
+{
+  uint_t err = AUBIO_OK;
+  if (p->rb) {
+    rubberband_reset(p->rb);
+  }
+  return err;
+}
+
+#endif
index 364a2c3..5c9561e 100644 (file)
@@ -53,8 +53,86 @@ struct _aubio_sink_t {
   del_aubio_sink_t s_del;
 };
 
+extern uint_t aubio_str_path_has_extension(const char_t *filename,
+    const char_t *pattern);
+
+#ifdef HAVE_VORBISENC
+typedef struct _aubio_sink_vorbis_t aubio_sink_vorbis_t;
+extern aubio_sink_vorbis_t * new_aubio_sink_vorbis(const char_t *uri,
+    uint_t samplerate);
+extern void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_close(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
+    uint_t channels);
+extern uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
+    uint_t samplerate);
+extern uint_t aubio_sink_vorbis_get_channels(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_get_samplerate(aubio_sink_vorbis_t *s);
+extern void aubio_sink_vorbis_do(aubio_sink_vorbis_t *s, fvec_t*
+    write_data, uint_t write);
+extern void aubio_sink_vorbis_do_multi(aubio_sink_vorbis_t *s, fmat_t*
+    write_data, uint_t write);
+#endif /* HAVE_VORBISENC */
+
+#ifdef HAVE_FLAC
+typedef struct _aubio_sink_flac_t aubio_sink_flac_t;
+extern aubio_sink_flac_t * new_aubio_sink_flac(const char_t *uri,
+    uint_t samplerate);
+extern void del_aubio_sink_flac (aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_open(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_close(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
+    uint_t channels);
+extern uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
+    uint_t samplerate);
+extern uint_t aubio_sink_flac_get_channels(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_get_samplerate(aubio_sink_flac_t *s);
+extern void aubio_sink_flac_do(aubio_sink_flac_t *s, fvec_t*
+    write_data, uint_t write);
+extern void aubio_sink_flac_do_multi(aubio_sink_flac_t *s, fmat_t*
+    write_data, uint_t write);
+#endif /* HAVE_FLAC */
+
 aubio_sink_t * new_aubio_sink(const char_t * uri, uint_t samplerate) {
   aubio_sink_t * s = AUBIO_NEW(aubio_sink_t);
+
+#ifdef HAVE_VORBISENC
+  // check if this uri could be for us
+  if (aubio_str_path_has_extension(uri, "ogg")) {
+    s->sink = (void *)new_aubio_sink_vorbis(uri, samplerate);
+    if (s->sink) {
+      s->s_do = (aubio_sink_do_t)(aubio_sink_vorbis_do);
+      s->s_do_multi = (aubio_sink_do_multi_t)(aubio_sink_vorbis_do_multi);
+      s->s_preset_samplerate = (aubio_sink_preset_samplerate_t)(aubio_sink_vorbis_preset_samplerate);
+      s->s_preset_channels = (aubio_sink_preset_channels_t)(aubio_sink_vorbis_preset_channels);
+      s->s_get_samplerate = (aubio_sink_get_samplerate_t)(aubio_sink_vorbis_get_samplerate);
+      s->s_get_channels = (aubio_sink_get_channels_t)(aubio_sink_vorbis_get_channels);
+      s->s_close = (aubio_sink_close_t)(aubio_sink_vorbis_close);
+      s->s_del = (del_aubio_sink_t)(del_aubio_sink_vorbis);
+      return s;
+    }
+  }
+#endif /* HAVE_VORBISENC */
+
+#ifdef HAVE_FLAC
+  // check if this uri could be for us
+  if (aubio_str_path_has_extension(uri, "flac")) {
+    s->sink = (void *)new_aubio_sink_flac(uri, samplerate);
+    if (s->sink) {
+      s->s_do = (aubio_sink_do_t)(aubio_sink_flac_do);
+      s->s_do_multi = (aubio_sink_do_multi_t)(aubio_sink_flac_do_multi);
+      s->s_preset_samplerate = (aubio_sink_preset_samplerate_t)(aubio_sink_flac_preset_samplerate);
+      s->s_preset_channels = (aubio_sink_preset_channels_t)(aubio_sink_flac_preset_channels);
+      s->s_get_samplerate = (aubio_sink_get_samplerate_t)(aubio_sink_flac_get_samplerate);
+      s->s_get_channels = (aubio_sink_get_channels_t)(aubio_sink_flac_get_channels);
+      s->s_close = (aubio_sink_close_t)(aubio_sink_flac_close);
+      s->s_del = (del_aubio_sink_t)(del_aubio_sink_flac);
+      return s;
+    }
+  }
+#endif /* HAVE_FLAC */
+
 #ifdef HAVE_SINK_APPLE_AUDIO
   s->sink = (void *)new_aubio_sink_apple_audio(uri, samplerate);
   if (s->sink) {
@@ -99,7 +177,9 @@ aubio_sink_t * new_aubio_sink(const char_t * uri, uint_t samplerate) {
 #endif /* HAVE_WAVWRITE */
 #if !defined(HAVE_WAVWRITE) && \
   !defined(HAVE_SNDFILE) && \
-  !defined(HAVE_SINK_APPLE_AUDIO)
+  !defined(HAVE_SINK_APPLE_AUDIO) && \
+  !defined(HAVE_VORBISENC) && \
+  !defined(HAVE_FLAC)
   AUBIO_ERROR("sink: failed creating '%s' at %dHz (no sink built-in)\n", uri, samplerate);
 #endif
   del_aubio_sink(s);
index c58a52c..5792a4f 100644 (file)
@@ -38,6 +38,10 @@ char_t *getPrintableOSStatusError(char_t *str, OSStatus error);
 
 uint_t aubio_sink_apple_audio_open(aubio_sink_apple_audio_t *s);
 
+uint_t aubio_str_extension_matches(const char_t *ext,
+    const char_t *pattern);
+const char_t *aubio_str_get_extension(const char_t *filename);
+
 #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);
@@ -52,6 +56,7 @@ struct _aubio_sink_apple_audio_t {
   AudioBufferList bufferList;
   ExtAudioFileRef audioFile;
   bool async;
+  AudioFileTypeID fileType;
 };
 
 aubio_sink_apple_audio_t * new_aubio_sink_apple_audio(const char_t * uri, uint_t samplerate) {
@@ -70,6 +75,8 @@ aubio_sink_apple_audio_t * new_aubio_sink_apple_audio(const char_t * uri, uint_t
   s->samplerate = 0;
   s->channels = 0;
 
+  aubio_sink_apple_audio_preset_format(s, aubio_str_get_extension(uri));
+
   // zero samplerate given. do not open yet
   if ((sint_t)samplerate == 0) {
     return s;
@@ -120,6 +127,74 @@ uint_t aubio_sink_apple_audio_preset_channels(aubio_sink_apple_audio_t *s, uint_
   return AUBIO_OK;
 }
 
+uint_t aubio_sink_apple_audio_preset_format(aubio_sink_apple_audio_t *s,
+    const char_t *fmt)
+{
+  if (aubio_str_extension_matches(fmt, "wav")) {
+    s->fileType = kAudioFileWAVEType;
+  } else if (aubio_str_extension_matches(fmt, "m4a")
+      || aubio_str_extension_matches(fmt, "mp4") ) {
+    // use alac for "mp4" and "m4a"
+    s->fileType = kAudioFileM4AType;
+  } else if (aubio_str_extension_matches(fmt, "aac") ) {
+    // only use lossy codec for "aac"
+    s->fileType = kAudioFileMPEG4Type;
+  } else if (aubio_str_extension_matches(fmt, "aiff") ) {
+    // only use lossy codec for "aac"
+    s->fileType = kAudioFileAIFFType;
+  } else {
+    s->fileType = kAudioFileWAVEType;
+    if (fmt && strnlen(fmt, PATH_MAX)) {
+      AUBIO_WRN("sink_apple_audio: could not guess format for %s,"
+         " using default (wav)\n", s->path);
+      return AUBIO_FAIL;
+    }
+  }
+  return AUBIO_OK;
+}
+
+static void aubio_sink_apple_audio_set_client_format(aubio_sink_apple_audio_t* s,
+    AudioStreamBasicDescription *clientFormat)
+{
+  memset(clientFormat, 0, sizeof(AudioStreamBasicDescription));
+  // always set samplerate and channels first
+  clientFormat->mSampleRate       = (Float64)(s->samplerate);
+  clientFormat->mChannelsPerFrame = s->channels;
+
+  switch (s->fileType) {
+    case kAudioFileM4AType:
+      clientFormat->mFormatID         = kAudioFormatAppleLossless;
+      break;
+    case kAudioFileMPEG4Type:
+      clientFormat->mFormatID         = kAudioFormatMPEG4AAC;
+      clientFormat->mFormatFlags      = kMPEG4Object_AAC_Main;
+      clientFormat->mFormatFlags     |= kAppleLosslessFormatFlag_16BitSourceData;
+      clientFormat->mFramesPerPacket  = 1024;
+      break;
+    case kAudioFileWAVEType:
+      clientFormat->mFormatID         = kAudioFormatLinearPCM;
+      clientFormat->mFormatFlags      = kAudioFormatFlagIsSignedInteger;
+      clientFormat->mFormatFlags     |= kAudioFormatFlagIsPacked;
+      clientFormat->mBitsPerChannel   = sizeof(short) * 8;
+      clientFormat->mFramesPerPacket  = 1;
+      clientFormat->mBytesPerFrame    = clientFormat->mBitsPerChannel * clientFormat->mChannelsPerFrame / 8;
+      clientFormat->mBytesPerPacket   = clientFormat->mFramesPerPacket * clientFormat->mBytesPerFrame;
+      break;
+    case kAudioFileAIFFType:
+      clientFormat->mFormatID         = kAudioFormatLinearPCM;
+      clientFormat->mFormatFlags      = kAudioFormatFlagIsSignedInteger;
+      clientFormat->mFormatFlags     |= kAudioFormatFlagIsPacked;
+      clientFormat->mFormatFlags     |= kAudioFormatFlagIsBigEndian;
+      clientFormat->mBitsPerChannel   = sizeof(short) * 8;
+      clientFormat->mFramesPerPacket  = 1;
+      clientFormat->mBytesPerFrame    = clientFormat->mBitsPerChannel * clientFormat->mChannelsPerFrame / 8;
+      clientFormat->mBytesPerPacket   = clientFormat->mFramesPerPacket * clientFormat->mBytesPerFrame;
+      break;
+    default:
+      break;
+  }
+}
+
 uint_t aubio_sink_apple_audio_get_samplerate(const aubio_sink_apple_audio_t *s)
 {
   return s->samplerate;
@@ -134,19 +209,6 @@ uint_t aubio_sink_apple_audio_open(aubio_sink_apple_audio_t *s) {
 
   if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
 
-  AudioStreamBasicDescription clientFormat;
-  memset(&clientFormat, 0, sizeof(AudioStreamBasicDescription));
-  clientFormat.mFormatID         = kAudioFormatLinearPCM;
-  clientFormat.mSampleRate       = (Float64)(s->samplerate);
-  clientFormat.mFormatFlags      = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
-  clientFormat.mChannelsPerFrame = s->channels;
-  clientFormat.mBitsPerChannel   = sizeof(short) * 8;
-  clientFormat.mFramesPerPacket  = 1;
-  clientFormat.mBytesPerFrame    = clientFormat.mBitsPerChannel * clientFormat.mChannelsPerFrame / 8;
-  clientFormat.mBytesPerPacket   = clientFormat.mFramesPerPacket * clientFormat.mBytesPerFrame;
-  clientFormat.mReserved         = 0;
-
-  AudioFileTypeID fileType = kAudioFileWAVEType;
   CFURLRef fileURL = createURLFromPath(s->path);
   bool overwrite = true;
 
@@ -161,8 +223,13 @@ uint_t aubio_sink_apple_audio_open(aubio_sink_apple_audio_t *s) {
   inputFormat.mFramesPerPacket  = 1;
   inputFormat.mBytesPerFrame    = inputFormat.mBitsPerChannel * inputFormat.mChannelsPerFrame / 8;
   inputFormat.mBytesPerPacket   = inputFormat.mFramesPerPacket * inputFormat.mBytesPerFrame;
+
+  // get the in-file format
+  AudioStreamBasicDescription clientFormat;
+  aubio_sink_apple_audio_set_client_format(s, &clientFormat);
+
   OSStatus err = noErr;
-  err = ExtAudioFileCreateWithURL(fileURL, fileType, &clientFormat, NULL,
+  err = ExtAudioFileCreateWithURL(fileURL, s->fileType, &clientFormat, NULL,
      overwrite ? kAudioFileFlags_EraseFile : 0, &s->audioFile);
   CFRelease(fileURL);
   if (err) {
@@ -173,6 +240,20 @@ uint_t aubio_sink_apple_audio_open(aubio_sink_apple_audio_t *s) {
     goto beach;
   }
 
+#if defined(kAppleSoftwareAudioCodecManufacturer)
+  // on iOS, set software based encoding before setting clientDataFormat
+  UInt32 codecManf = kAppleSoftwareAudioCodecManufacturer;
+  err = ExtAudioFileSetProperty(s->audioFile,
+      kExtAudioFileProperty_CodecManufacturer,
+      sizeof(UInt32), &codecManf);
+  if (err) {
+    char_t errorstr[20];
+    AUBIO_ERR("sink_apple_audio: error when trying to set sofware codec on %s "
+        "(%s)\n", s->path, getPrintableOSStatusError(errorstr, err));
+    goto beach;
+  }
+#endif
+
   err = ExtAudioFileSetProperty(s->audioFile,
       kExtAudioFileProperty_ClientDataFormat,
       sizeof(AudioStreamBasicDescription), &inputFormat);
@@ -237,9 +318,9 @@ void aubio_sink_apple_audio_write(aubio_sink_apple_audio_t *s, uint_t write) {
     if (err) {
       char_t errorstr[20];
       if (err == kExtAudioFileError_AsyncWriteBufferOverflow) {
-        sprintf(errorstr,"buffer overflow");
+        snprintf(errorstr, sizeof (errorstr), "buffer overflow");
       } else if (err == kExtAudioFileError_AsyncWriteTooLarge) {
-        sprintf(errorstr,"write too large");
+        snprintf(errorstr, sizeof (errorstr), "write too large");
       } else {
         // unknown error
         getPrintableOSStatusError(errorstr, err);
index 14d6a8a..1e228bc 100644 (file)
@@ -96,6 +96,29 @@ uint_t aubio_sink_apple_audio_preset_channels(aubio_sink_apple_audio_t *s, uint_
 
 /**
 
+  preset sink format
+
+  \param s sink, created with ::new_aubio_sink_apple_audio
+  \param fmt format of the file to create
+
+  \return 0 on success, 1 on error
+
+  Preset the format of the sink. Supported format strings:
+   - "wav": WAVE, 16 bit (default)
+   - "aiff": AIFF, 16 bit
+   - "m4a" or "mp4": Apple Audio Lossless Codec (ALAC)
+   - "aac": Audio Advanced Codec, lossy
+
+  Full list of supported encoding format is available in Table 1-2 of
+  `Multimedia Programming Guide
+  <https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/MultimediaPG/UsingAudio/UsingAudio.html>`_.
+
+ */
+uint_t aubio_sink_apple_audio_preset_format(aubio_sink_apple_audio_t *s,
+    const char_t *fmt);
+
+/**
+
   get samplerate of sink object
 
   \param s sink object, created with ::new_aubio_sink_apple_audio
diff --git a/src/io/sink_flac.c b/src/io/sink_flac.c
new file mode 100644 (file)
index 0000000..0414479
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+  This file is largely inspired by `examples/c/encode/file/main.c` in the
+  flac source package (versions 1.3.2 and later) available online at
+  https://xiph.org/flac/
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_FLAC
+
+#include "fmat.h"
+#include "io/ioutils.h"
+
+#include <FLAC/metadata.h>
+#include <FLAC/stream_encoder.h>
+
+#define MAX_WRITE_SIZE 4096
+
+// swap host to little endian
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define HTOLES(x) SWAPS(x)
+#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define HTOLES(x) x
+#else
+#ifdef HAVE_WIN_HACKS
+#define HTOLES(x) x
+#else
+#define HTOLES(x) SWAPS(htons(x))
+#endif
+#endif
+
+// convert to short, taking care of endianness
+#define FLOAT_TO_SHORT(x) (HTOLES((FLAC__int32)(x * 32768)))
+
+struct _aubio_sink_flac_t {
+  uint_t samplerate;
+  uint_t channels;
+  char_t *path;
+
+  FILE *fid;            // file id
+  FLAC__StreamEncoder* encoder;
+  FLAC__int32 *buffer;
+  FLAC__StreamMetadata **metadata;
+};
+
+typedef struct _aubio_sink_flac_t aubio_sink_flac_t;
+
+uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
+    uint_t channels);
+uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
+    uint_t samplerate);
+uint_t aubio_sink_flac_open(aubio_sink_flac_t *s);
+uint_t aubio_sink_flac_close (aubio_sink_flac_t *s);
+void del_aubio_sink_flac (aubio_sink_flac_t *s);
+
+#if 0
+static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder,
+    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
+    unsigned frames_writtten, unsigned total_frames_estimate,
+    void *client_data);
+#endif
+
+aubio_sink_flac_t * new_aubio_sink_flac (const char_t *uri,
+    uint_t samplerate)
+{
+  aubio_sink_flac_t * s = AUBIO_NEW(aubio_sink_flac_t);
+
+  if (!uri) {
+    AUBIO_ERROR("sink_flac: Aborted opening null path\n");
+    goto failure;
+  }
+
+  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
+  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
+  s->path[strnlen(uri, PATH_MAX)] = '\0';
+
+  s->channels = 0;
+  s->samplerate = 0;
+
+  if ((sint_t)samplerate == 0)
+    return s;
+
+  aubio_sink_flac_preset_samplerate(s, samplerate);
+  s->channels = 1;
+
+  if (aubio_sink_flac_open(s) != AUBIO_OK)
+    goto failure;
+
+  return s;
+
+failure:
+  del_aubio_sink_flac(s);
+  return NULL;
+}
+
+void del_aubio_sink_flac (aubio_sink_flac_t *s)
+{
+  if (s->fid)
+    aubio_sink_flac_close(s);
+  if (s->buffer)
+    AUBIO_FREE(s->buffer);
+  if (s->path)
+    AUBIO_FREE(s->path);
+  AUBIO_FREE(s);
+}
+
+uint_t aubio_sink_flac_open(aubio_sink_flac_t *s)
+{
+  uint_t ret = AUBIO_FAIL;
+  FLAC__bool ok = true;
+  FLAC__StreamEncoderInitStatus init_status;
+  FLAC__StreamMetadata_VorbisComment_Entry entry;
+  const unsigned comp_level = 5;
+  const unsigned bps = 16;
+
+  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
+
+  s->buffer = AUBIO_ARRAY(FLAC__int32, s->channels * MAX_WRITE_SIZE);
+  if (!s->buffer) {
+    AUBIO_ERR("sink_flac: failed allocating buffer for %s\n", s->path);
+    return AUBIO_FAIL;
+  }
+
+  s->fid = fopen((const char *)s->path, "wb");
+  if (!s->fid) {
+    AUBIO_STRERR("sink_flac: Failed opening %s (%s)\n", s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+
+  if((s->encoder = FLAC__stream_encoder_new()) == NULL) {
+    AUBIO_ERR("sink_flac: failed allocating encoder for %s\n", s->path);
+    goto failure;
+  }
+  ok &= FLAC__stream_encoder_set_verify(s->encoder, true);
+  ok &= FLAC__stream_encoder_set_compression_level(s->encoder, comp_level);
+  ok &= FLAC__stream_encoder_set_channels(s->encoder, s->channels);
+  ok &= FLAC__stream_encoder_set_bits_per_sample(s->encoder, bps);
+  ok &= FLAC__stream_encoder_set_sample_rate(s->encoder, s->samplerate);
+  // the total number of samples can not be estimated (streaming)
+  // it will be set by the encoder in FLAC__stream_encoder_finish
+  //ok &= FLAC__stream_encoder_set_total_samples_estimate(s->encoder, 0);
+
+  if (!ok) {
+    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
+    goto failure;
+  }
+
+  s->metadata = AUBIO_ARRAY(FLAC__StreamMetadata*, 2);
+  if (!s->metadata) {
+    AUBIO_ERR("sink_flac: failed allocating memory for %s\n", s->path);
+    goto failure;
+  }
+
+  s->metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+  if (!s->metadata[0]) {
+    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
+    goto failure;
+  }
+
+  s->metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+  if (!s->metadata[1]) {
+    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
+    goto failure;
+  }
+
+  ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry,
+      "encoder", "aubio");
+  ok &= FLAC__metadata_object_vorbiscomment_append_comment(s->metadata[0],
+      entry, false);
+  if (!ok) {
+    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
+    goto failure;
+  }
+
+  // padding length
+  s->metadata[1]->length = 1234;
+  if (!FLAC__stream_encoder_set_metadata(s->encoder, s->metadata, 2)) {
+    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
+    goto failure;
+  }
+
+  // initialize encoder
+  init_status = FLAC__stream_encoder_init_file(s->encoder, s->path,
+      NULL, NULL);
+      //aubio_sink_flac_callback, s);
+  if (init_status == FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE) {
+    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
+       " (invalid samplerate %d)\n", s->path, s->samplerate);
+    goto failure;
+  } else if (init_status ==
+      FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS) {
+    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
+       " (invalid number of channel %d)\n", s->path, s->channels);
+    goto failure;
+  } else if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
+    AUBIO_ERR("sink_flac: failed initilizing encoder for %s (%d)\n",
+        s->path, (int)init_status);
+    goto failure;
+  }
+
+  // mark success
+  ret = AUBIO_OK;
+
+failure:
+
+  return ret;
+}
+
+uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
+    uint_t samplerate)
+{
+  if (aubio_io_validate_samplerate("sink_flac", s->path, samplerate))
+    return AUBIO_FAIL;
+  s->samplerate = samplerate;
+  if (s->samplerate != 0 && s->channels != 0)
+    return aubio_sink_flac_open(s);
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
+    uint_t channels)
+{
+  if (aubio_io_validate_channels("sink_flac", 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) {
+    return aubio_sink_flac_open(s);
+  }
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_flac_get_samplerate(const aubio_sink_flac_t *s)
+{
+  return s->samplerate;
+}
+
+uint_t aubio_sink_flac_get_channels(const aubio_sink_flac_t *s)
+{
+  return s->channels;
+}
+
+static void aubio_sink_write_frames(aubio_sink_flac_t *s, uint_t length)
+{
+  // send to encoder
+  if (!FLAC__stream_encoder_process_interleaved(s->encoder,
+        (const FLAC__int32*)s->buffer, length)) {
+    FLAC__StreamEncoderState state =
+      FLAC__stream_encoder_get_state(s->encoder);
+    AUBIO_WRN("sink_flac: error writing to %s (%s)\n",
+        s->path, FLAC__StreamEncoderStateString[state]);
+  }
+}
+
+void aubio_sink_flac_do(aubio_sink_flac_t *s, fvec_t *write_data,
+    uint_t write)
+{
+  uint_t c, v;
+  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
+      MAX_WRITE_SIZE, write_data->length, write);
+  // fill buffer
+  if (!write) {
+    return;
+  } else {
+    for (c = 0; c < s->channels; c++) {
+      for (v = 0; v < length; v++) {
+        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[v]);
+      }
+    }
+  }
+  // send to encoder
+  aubio_sink_write_frames(s, length);
+}
+
+void aubio_sink_flac_do_multi(aubio_sink_flac_t *s, fmat_t *write_data,
+    uint_t write)
+{
+  uint_t c, v;
+  uint_t channels = aubio_sink_validate_input_channels("sink_flac", s->path,
+      s->channels, write_data->height);
+  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
+      MAX_WRITE_SIZE, write_data->length, write);
+  // fill buffer
+  if (!write) {
+    return;
+  } else {
+    for (c = 0; c < channels; c++) {
+      for (v = 0; v < length; v++) {
+        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[c][v]);
+      }
+    }
+  }
+  // send to encoder
+  aubio_sink_write_frames(s, length);
+}
+
+uint_t aubio_sink_flac_close (aubio_sink_flac_t *s)
+{
+  uint_t ret = AUBIO_OK;
+
+  if (!s->fid) return AUBIO_FAIL;
+
+  if (s->encoder) {
+    // mark the end of stream
+    if (!FLAC__stream_encoder_finish(s->encoder)) {
+      FLAC__StreamEncoderState state =
+        FLAC__stream_encoder_get_state(s->encoder);
+      AUBIO_ERR("sink_flac: Error closing encoder for %s (%s)\n",
+          s->path, FLAC__StreamEncoderStateString[state]);
+      ret &= AUBIO_FAIL;
+    }
+
+    FLAC__stream_encoder_delete(s->encoder);
+  }
+
+  if (s->metadata) {
+    // clean up metadata after stream finished
+    if (s->metadata[0])
+      FLAC__metadata_object_delete(s->metadata[0]);
+    if (s->metadata[1])
+      FLAC__metadata_object_delete(s->metadata[1]);
+    AUBIO_FREE(s->metadata);
+  }
+
+  if (s->fid && fclose(s->fid)) {
+    AUBIO_STRERR("sink_flac: Error closing file %s (%s)\n", s->path, errorstr);
+    ret &= AUBIO_FAIL;
+  }
+  s->fid = NULL;
+
+  return ret;
+}
+
+#if 0
+static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder UNUSED,
+    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
+    unsigned frames_written, unsigned total_frames_estimate,
+    void *client_data UNUSED)
+{
+  AUBIO_WRN("sink_flac: %d bytes_written, %d samples_written,"
+      " %d/%d frames writen\n",
+      bytes_written, samples_written, frames_written, total_frames_estimate);
+}
+#endif
+
+#endif /* HAVE_FLAC */
index 35e2215..5b168a4 100644 (file)
@@ -48,10 +48,15 @@ struct _aubio_sink_sndfile_t {
   SNDFILE *handle;
   uint_t scratch_size;
   smpl_t *scratch_data;
+  int format;
 };
 
 uint_t aubio_sink_sndfile_open(aubio_sink_sndfile_t *s);
 
+uint_t aubio_str_extension_matches(const char_t *ext,
+    const char_t *pattern);
+const char_t *aubio_str_get_extension(const char_t *filename);
+
 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;
@@ -67,6 +72,8 @@ aubio_sink_sndfile_t * new_aubio_sink_sndfile(const char_t * path, uint_t sample
   s->samplerate = 0;
   s->channels = 0;
 
+  aubio_sink_sndfile_preset_format(s, aubio_str_get_extension(path));
+
   // zero samplerate given. do not open yet
   if ((sint_t)samplerate == 0) {
     return s;
@@ -115,6 +122,30 @@ uint_t aubio_sink_sndfile_preset_channels(aubio_sink_sndfile_t *s, uint_t channe
   return AUBIO_OK;
 }
 
+uint_t aubio_sink_sndfile_preset_format(aubio_sink_sndfile_t *s,
+    const char_t *fmt)
+{
+  if (aubio_str_extension_matches(fmt, "wav")) {
+    s->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+  } else if (aubio_str_extension_matches(fmt, "aiff")) {
+    s->format = SF_FORMAT_AIFF | SF_FORMAT_PCM_16;
+  } else if (aubio_str_extension_matches(fmt, "flac")) {
+    s->format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
+  } else if (aubio_str_extension_matches(fmt, "ogg")) {
+    s->format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
+  } else if (atoi(fmt) > 0x010000) {
+    s->format = atoi(fmt);
+  } else {
+    s->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+    if (fmt && strnlen(fmt, PATH_MAX))  {
+      AUBIO_WRN("sink_sndfile: could not guess format %s for %s,"
+          " using default (wav)\n", fmt, s->path);
+      return AUBIO_FAIL;
+    }
+  }
+  return AUBIO_OK;
+}
+
 uint_t aubio_sink_sndfile_get_samplerate(const aubio_sink_sndfile_t *s)
 {
   return s->samplerate;
@@ -131,7 +162,7 @@ uint_t aubio_sink_sndfile_open(aubio_sink_sndfile_t *s) {
   AUBIO_MEMSET(&sfinfo, 0, sizeof (sfinfo));
   sfinfo.samplerate = s->samplerate;
   sfinfo.channels   = s->channels;
-  sfinfo.format     = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+  sfinfo.format     = s->format;
 
   /* try creating the file */
   s->handle = sf_open (s->path, SFM_WRITE, &sfinfo);
index 083c4ca..d12ff55 100644 (file)
@@ -95,6 +95,39 @@ uint_t aubio_sink_sndfile_preset_channels(aubio_sink_sndfile_t *s, uint_t channe
 
 /**
 
+  preset sink format
+
+  \param s sink, created with ::new_aubio_sink_sndfile
+  \param fmt format of the file to create
+
+  \return 0 on success, 1 on error
+
+  Preset the format of the sink. Supported format strings:
+   - "wav": 16 bit (default)
+   - "aiff": aiff, 16 bit
+   - "flac": flac, 16 bit
+   - "ogg": ogg vorbis stream
+
+  Alternatively, any sndfile format can be set by passing the corresponding
+  integer as a string:
+
+  \code{.c}
+  char_t fmt[10];
+  snprintf(fmt, sizeof(fmt), "%d", SF_FORMAT_FLAC | SF_FORMAT_PCM_24);
+  aubio_sink_sndfile_preset_format(s, fmt);
+  \endcode
+
+  The file should have been created using a samplerate of 0.
+
+  This function should be called before aubio_sink_sndfile_preset_samplerate()
+  and aubio_sink_sndfile_preset_channels().
+
+*/
+uint_t aubio_sink_sndfile_preset_format(aubio_sink_sndfile_t *s,
+        const char_t* fmt);
+
+/**
+
   get samplerate of sink object
 
   \param s sink object, created with ::new_aubio_sink_sndfile
diff --git a/src/io/sink_vorbis.c b/src/io/sink_vorbis.c
new file mode 100644 (file)
index 0000000..00f3e1c
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+  This file is largely inspired by `examples/encoder_example.c` in the
+  libvorbis source package (versions 1.3.5 and later) available online at
+  https://xiph.org/vorbis/
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_VORBISENC
+
+#include "fmat.h"
+#include "io/ioutils.h"
+
+#include <vorbis/vorbisenc.h>
+#include <string.h> // strerror
+#include <errno.h> // errno
+#include <time.h> // time
+
+#define MAX_SIZE 4096
+
+struct _aubio_sink_vorbis_t {
+  FILE *fid;            // file id
+  ogg_stream_state os;  // stream
+  ogg_page og;          // page
+  ogg_packet op;        // data packet
+  vorbis_info vi;       // vorbis bitstream settings
+  vorbis_comment vc;    // user comment
+  vorbis_dsp_state vd;  // working state
+  vorbis_block vb;      // working space
+
+  uint_t samplerate;
+  uint_t channels;
+  char_t *path;
+};
+
+typedef struct _aubio_sink_vorbis_t aubio_sink_vorbis_t;
+
+uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
+    uint_t channels);
+uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
+    uint_t samplerate);
+uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s);
+uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s);
+void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s);
+
+static uint_t aubio_sink_vorbis_write_page(aubio_sink_vorbis_t *s);
+
+aubio_sink_vorbis_t * new_aubio_sink_vorbis (const char_t *uri,
+    uint_t samplerate)
+{
+  aubio_sink_vorbis_t * s = AUBIO_NEW(aubio_sink_vorbis_t);
+
+  if (!uri) {
+    AUBIO_ERROR("sink_vorbis: Aborted opening null path\n");
+    goto failure;
+  }
+
+  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
+  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
+  s->path[strnlen(uri, PATH_MAX)] = '\0';
+
+  s->channels = 0;
+
+  if ((sint_t)samplerate == 0)
+    return s;
+
+  aubio_sink_vorbis_preset_samplerate(s, samplerate);
+  s->channels = 1;
+
+  if (aubio_sink_vorbis_open(s) != AUBIO_OK)
+    goto failure;
+
+  return s;
+
+failure:
+  del_aubio_sink_vorbis(s);
+  return NULL;
+}
+
+void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s)
+{
+  if (s->fid) aubio_sink_vorbis_close(s);
+  // clean up
+  ogg_stream_clear(&s->os);
+  vorbis_block_clear(&s->vb);
+  vorbis_dsp_clear(&s->vd);
+  vorbis_comment_clear(&s->vc);
+  vorbis_info_clear(&s->vi);
+
+  if (s->path) AUBIO_FREE(s->path);
+  AUBIO_FREE(s);
+}
+
+uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s)
+{
+  float quality_mode = .9;
+
+  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
+
+  s->fid = fopen((const char *)s->path, "wb");
+  if (!s->fid) {
+    AUBIO_STRERR("sink_vorbis: Error opening file \'%s\' (%s)\n",
+        s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+
+  vorbis_info_init(&s->vi);
+  if (vorbis_encode_init_vbr(&s->vi, s->channels, s->samplerate, quality_mode))
+  {
+    AUBIO_ERR("sink_vorbis: vorbis_encode_init_vbr failed\n");
+    return AUBIO_FAIL;
+  }
+
+  // add comment
+  vorbis_comment_init(&s->vc);
+  vorbis_comment_add_tag(&s->vc, "ENCODER", "aubio");
+
+  // initalise analysis and block
+  vorbis_analysis_init(&s->vd, &s->vi);
+  vorbis_block_init(&s->vd, &s->vb);
+
+  // pick randome serial number
+  srand(time(NULL));
+  ogg_stream_init(&s->os, rand());
+
+  // write header
+  {
+    ogg_packet header;
+    ogg_packet header_comm;
+    ogg_packet header_code;
+
+    vorbis_analysis_headerout(&s->vd, &s->vc, &header, &header_comm,
+        &header_code);
+
+    ogg_stream_packetin(&s->os, &header);
+    ogg_stream_packetin(&s->os, &header_comm);
+    ogg_stream_packetin(&s->os, &header_code);
+
+    // make sure audio data will start on a new page
+    while (1)
+    {
+      if (!ogg_stream_flush(&s->os, &s->og)) break;
+      if (aubio_sink_vorbis_write_page(s)) return AUBIO_FAIL;
+    }
+  }
+
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
+    uint_t samplerate)
+{
+  if (aubio_io_validate_samplerate("sink_vorbis", s->path, samplerate))
+    return AUBIO_FAIL;
+  s->samplerate = samplerate;
+  if (/* s->samplerate != 0 && */ s->channels != 0)
+    return aubio_sink_vorbis_open(s);
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
+    uint_t channels)
+{
+  if (aubio_io_validate_channels("sink_vorbis", 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 */) {
+    return aubio_sink_vorbis_open(s);
+  }
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_vorbis_get_samplerate(const aubio_sink_vorbis_t *s)
+{
+  return s->samplerate;
+}
+
+uint_t aubio_sink_vorbis_get_channels(const aubio_sink_vorbis_t *s)
+{
+  return s->channels;
+}
+
+static
+uint_t aubio_sink_vorbis_write_page(aubio_sink_vorbis_t *s) {
+  int result;
+  size_t wrote;
+  wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
+  result = (wrote == (unsigned)s->og.header_len);
+  wrote = fwrite(s->og.body, 1, s->og.body_len,     s->fid);
+  result &= (wrote == (unsigned)s->og.body_len);
+  if (result == 0) {
+    AUBIO_STRERR("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
+        s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+  return AUBIO_OK;
+}
+
+static
+void aubio_sink_vorbis_write(aubio_sink_vorbis_t *s)
+{
+  // pre-analysis
+  while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
+
+    vorbis_analysis(&s->vb, NULL);
+    vorbis_bitrate_addblock(&s->vb);
+
+    while (vorbis_bitrate_flushpacket(&s->vd, &s->op))
+    {
+      ogg_stream_packetin(&s->os, &s->op);
+
+      while (1) {
+        if (!ogg_stream_pageout (&s->os, &s->og)) break;
+        aubio_sink_vorbis_write_page(s);
+        if (ogg_page_eos(&s->og)) break;
+      }
+    }
+  }
+}
+
+void aubio_sink_vorbis_do(aubio_sink_vorbis_t *s, fvec_t *write_data,
+    uint_t write)
+{
+  uint_t c, v;
+  uint_t length = aubio_sink_validate_input_length("sink_vorbis", s->path,
+      MAX_SIZE, write_data->length, write);
+  float **buffer = vorbis_analysis_buffer(&s->vd, (long)length);
+  // fill buffer
+  if (!write) {
+    return;
+  } else if (!buffer) {
+    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
+    return;
+  } else {
+    for (c = 0; c < s->channels; c++) {
+      for (v = 0; v < length; v++) {
+        buffer[c][v] = write_data->data[v];
+      }
+    }
+    // tell vorbis how many frames were written
+    vorbis_analysis_wrote(&s->vd, (long)length);
+  }
+  // write to file
+  aubio_sink_vorbis_write(s);
+}
+
+void aubio_sink_vorbis_do_multi(aubio_sink_vorbis_t *s, fmat_t *write_data,
+    uint_t write)
+{
+  uint_t c, v;
+  uint_t channels = aubio_sink_validate_input_channels("sink_vorbis", s->path,
+      s->channels, write_data->height);
+  uint_t length = aubio_sink_validate_input_length("sink_vorbis", s->path,
+      MAX_SIZE, write_data->length, write);
+  float **buffer = vorbis_analysis_buffer(&s->vd, (long)length);
+  // fill buffer
+  if (!write) {
+    return;
+  } else if (!buffer) {
+    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
+    return;
+  } else {
+    for (c = 0; c < channels; c++) {
+      for (v = 0; v < length; v++) {
+        buffer[c][v] = write_data->data[c][v];
+      }
+    }
+    // tell vorbis how many frames were written
+    vorbis_analysis_wrote(&s->vd, (long)length);
+  }
+
+  aubio_sink_vorbis_write(s);
+}
+
+uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s)
+{
+  if (!s->fid) return AUBIO_FAIL;
+  //mark the end of stream
+  vorbis_analysis_wrote(&s->vd, 0);
+
+  aubio_sink_vorbis_write(s);
+
+  if (s->fid && fclose(s->fid)) {
+    AUBIO_STRERR("sink_vorbis: Error closing file \'%s\' (%s)\n",
+        s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+  s->fid = NULL;
+  return AUBIO_OK;
+}
+
+#endif /* HAVE_VORBISENC */
index 5b25d85..71ea8b5 100644 (file)
 
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
-#if defined(HAVE_SWRESAMPLE)
 #include <libswresample/swresample.h>
-#elif defined(HAVE_AVRESAMPLE)
-#include <libavresample/avresample.h>
-#endif
 #include <libavutil/opt.h>
 
 // determine whether we use libavformat from ffmpeg or from libav
 #define av_packet_unref av_free_packet
 #endif
 
+#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57,28,100)
+//#warning "libavutil < 57.28.100 is deprecated"
+#else
+#define LIBAVUTIL_HAS_CH_LAYOUT
+#endif
+
 #include "aubio_priv.h"
 #include "fvec.h"
 #include "fmat.h"
 #define AUBIO_AVCODEC_MAX_BUFFER_SIZE AV_INPUT_BUFFER_MIN_SIZE
 #endif
 
+#if LIBAVCODEC_VERSION_MAJOR >= 59
+#define FF_API_LAVF_AVCTX 1
+#endif
+
 struct _aubio_source_avcodec_t {
   uint_t hop_size;
   uint_t samplerate;
@@ -82,12 +88,12 @@ struct _aubio_source_avcodec_t {
   AVFormatContext *avFormatCtx;
   AVCodecContext *avCodecCtx;
   AVFrame *avFrame;
+#if FF_API_INIT_PACKET
+  AVPacket *avPacket;
+#else
   AVPacket avPacket;
-#ifdef HAVE_AVRESAMPLE
-  AVAudioResampleContext *avr;
-#elif defined(HAVE_SWRESAMPLE)
-  SwrContext *avr;
 #endif
+  SwrContext *avr;
   smpl_t *output;
   uint_t read_samples;
   uint_t read_index;
@@ -122,11 +128,15 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * path,
   AVFormatContext *avFormatCtx = NULL;
   AVCodecContext *avCodecCtx = NULL;
   AVFrame *avFrame = NULL;
+#if FF_API_INIT_PACKET
+  AVPacket *avPacket = NULL;
+#endif
   sint_t selected_stream = -1;
 #if FF_API_LAVF_AVCTX
   AVCodecParameters *codecpar;
 #endif
-  AVCodec *codec;
+
+  const AVCodec *codec;
   uint_t i;
   int err;
   if (path == NULL) {
@@ -259,7 +269,11 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * path,
 
   /* get input specs */
   s->input_samplerate = avCodecCtx->sample_rate;
+#ifdef LIBAVUTIL_HAS_CH_LAYOUT
+  s->input_channels   = avCodecCtx->ch_layout.nb_channels;
+#else
   s->input_channels   = avCodecCtx->channels;
+#endif
   //AUBIO_DBG("input_samplerate: %d\n", s->input_samplerate);
   //AUBIO_DBG("input_channels: %d\n", s->input_channels);
 
@@ -277,7 +291,16 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * path,
   avFrame = av_frame_alloc();
   if (!avFrame) {
     AUBIO_ERR("source_avcodec: Could not allocate frame for (%s)\n", s->path);
+    goto beach;
+  }
+
+#if FF_API_INIT_PACKET
+  avPacket = av_packet_alloc();
+  if (!avPacket) {
+    AUBIO_ERR("source_avcodec: Could not allocate packet for (%s)\n", s->path);
+    goto beach;
   }
+#endif
 
   /* allocate output for avr */
   s->output = (smpl_t *)av_malloc(AUBIO_AVCODEC_MAX_BUFFER_SIZE
@@ -289,6 +312,9 @@ aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * path,
   s->avFormatCtx = avFormatCtx;
   s->avCodecCtx = avCodecCtx;
   s->avFrame = avFrame;
+#if FF_API_INIT_PACKET
+  s->avPacket = avPacket;
+#endif
 
   aubio_source_avcodec_reset_resampler(s);
 
@@ -312,16 +338,22 @@ void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s)
   // create or reset resampler to/from mono/multi-channel
   if ( s->avr == NULL ) {
     int err;
+    SwrContext *avr = swr_alloc();
+#ifdef LIBAVUTIL_HAS_CH_LAYOUT
+    AVChannelLayout input_layout;
+    AVChannelLayout output_layout;
+    av_channel_layout_default(&input_layout, s->input_channels);
+    av_channel_layout_default(&output_layout, s->input_channels);
+
+    av_opt_set_chlayout(avr, "in_channel_layout",  &input_layout,        0);
+    av_opt_set_chlayout(avr, "out_channel_layout", &output_layout,       0);
+#else
     int64_t input_layout = av_get_default_channel_layout(s->input_channels);
     int64_t output_layout = av_get_default_channel_layout(s->input_channels);
-#ifdef HAVE_AVRESAMPLE
-    AVAudioResampleContext *avr = avresample_alloc_context();
-#elif defined(HAVE_SWRESAMPLE)
-    SwrContext *avr = swr_alloc();
-#endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
 
     av_opt_set_int(avr, "in_channel_layout",  input_layout,              0);
     av_opt_set_int(avr, "out_channel_layout", output_layout,             0);
+#endif
     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);
@@ -332,11 +364,7 @@ void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s)
 #endif
     // TODO: use planar?
     //av_opt_set_int(avr, "out_sample_fmt",     AV_SAMPLE_FMT_FLTP,      0);
-#ifdef HAVE_AVRESAMPLE
-    if ( ( err = avresample_open(avr) ) < 0)
-#elif defined(HAVE_SWRESAMPLE)
     if ( ( err = swr_init(avr) ) < 0)
-#endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
     {
       char errorstr[256];
       av_strerror (err, errorstr, sizeof(errorstr));
@@ -354,36 +382,34 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s,
   AVFormatContext *avFormatCtx = s->avFormatCtx;
   AVCodecContext *avCodecCtx = s->avCodecCtx;
   AVFrame *avFrame = s->avFrame;
-  AVPacket avPacket = s->avPacket;
-#ifdef HAVE_AVRESAMPLE
-  AVAudioResampleContext *avr = s->avr;
-#elif defined(HAVE_SWRESAMPLE)
+#if FF_API_INIT_PACKET
+  AVPacket *avPacket = s->avPacket;
+#else
+  AVPacket *avPacket = &s->avPacket;
+#endif
   SwrContext *avr = s->avr;
-#endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
   int got_frame = 0;
-#ifdef HAVE_AVRESAMPLE
-  int in_linesize = 0;
-  int in_samples = avFrame->nb_samples;
-  int out_linesize = 0;
-  int max_out_samples = AUBIO_AVCODEC_MAX_BUFFER_SIZE;
-  int out_samples = 0;
-#elif defined(HAVE_SWRESAMPLE)
   int in_samples = avFrame->nb_samples;
+#ifdef LIBAVUTIL_HAS_CH_LAYOUT
+  int max_out_samples = AUBIO_AVCODEC_MAX_BUFFER_SIZE / avCodecCtx->ch_layout.nb_channels;
+#else
   int max_out_samples = AUBIO_AVCODEC_MAX_BUFFER_SIZE / avCodecCtx->channels;
+#endif
   int out_samples = 0;
-#endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
   smpl_t *output = s->output;
 #ifndef FF_API_LAVF_AVCTX
   int len = 0;
 #else
   int ret = 0;
 #endif
-  av_init_packet (&avPacket);
+#ifndef FF_API_INIT_PACKET
+  av_init_packet (avPacket);
+#endif
   *read_samples = 0;
 
   do
   {
-    int err = av_read_frame (avFormatCtx, &avPacket);
+    int err = av_read_frame (avFormatCtx, avPacket);
     if (err == AVERROR_EOF) {
       s->eof = 1;
       goto beach;
@@ -396,10 +422,10 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s,
       s->eof = 1;
       goto beach;
     }
-  } while (avPacket.stream_index != s->selected_stream);
+  } while (avPacket->stream_index != s->selected_stream);
 
 #if FF_API_LAVF_AVCTX
-  ret = avcodec_send_packet(avCodecCtx, &avPacket);
+  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;
@@ -422,7 +448,7 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s,
     }
   }
 #else
-  len = avcodec_decode_audio4(avCodecCtx, avFrame, &got_frame, &avPacket);
+  len = avcodec_decode_audio4(avCodecCtx, avFrame, &got_frame, avPacket);
 
   if (len < 0) {
     AUBIO_ERR("source_avcodec: error while decoding %s\n", s->path);
@@ -436,33 +462,27 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s,
   }
 
 #if LIBAVUTIL_VERSION_MAJOR > 52
-  if (avFrame->channels != (sint_t)s->input_channels) {
+#ifdef LIBAVUTIL_HAS_CH_LAYOUT
+  int frame_channels = avFrame->ch_layout.nb_channels;
+#else
+  int frame_channels = avFrame->channels;
+#endif
+  if (frame_channels != (sint_t)s->input_channels) {
     AUBIO_WRN ("source_avcodec: trying to read from %d channel(s),"
         "but configured for %d; is '%s' corrupt?\n",
-        avFrame->channels, s->input_channels, s->path);
+        frame_channels, s->input_channels, s->path);
     goto beach;
   }
 #else
 #warning "avutil < 53 is deprecated, crashes might occur on corrupt files"
 #endif
 
-#ifdef HAVE_AVRESAMPLE
-  in_linesize = 0;
-  av_samples_get_buffer_size(&in_linesize, avCodecCtx->channels,
-      avFrame->nb_samples, avCodecCtx->sample_fmt, 1);
   in_samples = avFrame->nb_samples;
-  out_linesize = 0;
   max_out_samples = AUBIO_AVCODEC_MAX_BUFFER_SIZE;
-  out_samples = avresample_convert ( avr,
-        (uint8_t **)&output, out_linesize, max_out_samples,
-        (uint8_t **)avFrame->data, in_linesize, in_samples);
-#elif defined(HAVE_SWRESAMPLE)
-  in_samples = avFrame->nb_samples;
-  max_out_samples = AUBIO_AVCODEC_MAX_BUFFER_SIZE / avCodecCtx->channels;
+  if (frame_channels > 0) max_out_samples /= frame_channels;
   out_samples = swr_convert( avr,
       (uint8_t **)&output, max_out_samples,
       (const uint8_t **)avFrame->data, in_samples);
-#endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
   if (out_samples < 0) {
     AUBIO_WRN("source_avcodec: error while resampling %s (%d)\n",
         s->path, out_samples);
@@ -472,7 +492,7 @@ void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s,
   *read_samples = out_samples;
 
 beach:
-  av_packet_unref(&avPacket);
+  av_packet_unref(avPacket);
 }
 
 void aubio_source_avcodec_do(aubio_source_avcodec_t * s, fvec_t * read_data,
@@ -596,14 +616,8 @@ uint_t aubio_source_avcodec_seek (aubio_source_avcodec_t * s, uint_t pos) {
   s->eof = 0;
   s->read_index = 0;
   s->read_samples = 0;
-#ifdef HAVE_AVRESAMPLE
-  // reset the AVAudioResampleContext
-  avresample_close(s->avr);
-  avresample_open(s->avr);
-#elif defined(HAVE_SWRESAMPLE)
   swr_close(s->avr);
   swr_init(s->avr);
-#endif
   return ret;
 }
 
@@ -617,13 +631,8 @@ uint_t aubio_source_avcodec_get_duration (aubio_source_avcodec_t * s) {
 
 uint_t aubio_source_avcodec_close(aubio_source_avcodec_t * s) {
   if (s->avr != NULL) {
-#ifdef HAVE_AVRESAMPLE
-    avresample_close( s->avr );
-    av_free ( s->avr );
-#elif defined(HAVE_SWRESAMPLE)
     swr_close ( s->avr );
     swr_free ( &s->avr );
-#endif
   }
   s->avr = NULL;
   if (s->avCodecCtx != NULL) {
@@ -638,7 +647,13 @@ uint_t aubio_source_avcodec_close(aubio_source_avcodec_t * s) {
     avformat_close_input(&s->avFormatCtx);
     s->avFormatCtx = NULL;
   }
+#if FF_API_INIT_PACKET
+  if (s->avPacket) {
+    av_packet_unref(s->avPacket);
+  }
+#else
   av_packet_unref(&s->avPacket);
+#endif
   return AUBIO_OK;
 }
 
@@ -653,6 +668,12 @@ void del_aubio_source_avcodec(aubio_source_avcodec_t * s){
     av_frame_free( &(s->avFrame) );
   }
   s->avFrame = NULL;
+#if FF_API_INIT_PACKET
+  if (s->avPacket != NULL) {
+    av_packet_free( &(s->avPacket) );
+  }
+  s->avPacket = NULL;
+#endif
   if (s->path) {
     AUBIO_FREE(s->path);
   }
index a05d65d..29d884b 100644 (file)
@@ -52,7 +52,7 @@ char_t *getPrintableOSStatusError(char_t *str, OSStatus error)
         str[6] = '\0';
     } else
         // no, format it as an integer
-        sprintf(str, "%d", (int)error);
+        snprintf(str, 10, "%d", (int)error);
     return str;
 }
 
index a659495..af222e5 100644 (file)
@@ -240,7 +240,7 @@ smpl_t aubio_db_spl (const fvec_t * v);
   \param v vector to get level from
   \param threshold threshold in dB SPL
 
-  \return 0 if level is under the given threshold, 1 otherwise
+  \return 1 if level is under the given threshold, 0 otherwise
 
 */
 uint_t aubio_silence_detection (const fvec_t * v, smpl_t threshold);
diff --git a/src/utils/strutils.c b/src/utils/strutils.c
new file mode 100644 (file)
index 0000000..8b695ff
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_WIN_HACKS
+#define strncasecmp _strnicmp
+#endif
+
+const char_t *aubio_str_get_extension(const char_t *filename)
+{
+  // find last occurence of dot character
+  const char_t *ext;
+  if (!filename) return NULL;
+  ext = strrchr(filename, '.');
+  if (!ext || ext == filename) return "";
+  else return ext + 1;
+}
+
+uint_t aubio_str_extension_matches(const char_t *ext, const char_t *pattern)
+{
+  return ext && pattern && (strncasecmp(ext, pattern, PATH_MAX) == 0);
+}
+
+uint_t aubio_str_path_has_extension(const char_t *filename,
+    const char_t *pattern)
+{
+  const char_t *ext = aubio_str_get_extension(filename);
+  return aubio_str_extension_matches(ext, pattern);
+}
index 895c191..3130c65 100644 (file)
@@ -6,11 +6,13 @@ uselib += ['FFTW3', 'FFTW3F']
 uselib += ['INTEL_IPP']
 uselib += ['SAMPLERATE']
 uselib += ['SNDFILE']
+uselib += ['RUBBERBAND']
 uselib += ['AVCODEC']
 uselib += ['AVFORMAT']
 uselib += ['SWRESAMPLE']
-uselib += ['AVRESAMPLE']
 uselib += ['AVUTIL']
+uselib += ['VORBISENC']
+uselib += ['FLAC']
 uselib += ['BLAS']
 
 source = ctx.path.ant_glob('*.c **/*.c')
diff --git a/tests/src/effects/test-pitchshift.c b/tests/src/effects/test-pitchshift.c
new file mode 100644 (file)
index 0000000..64bce30
--- /dev/null
@@ -0,0 +1,110 @@
+#define AUBIO_UNSTABLE 1
+#include <aubio.h>
+#include "utils_tests.h"
+
+int test_wrong_params(void);
+
+int main (int argc, char **argv)
+{
+  sint_t err = 0;
+
+  if (argc < 3) {
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = test_wrong_params();
+    PRINT_MSG("usage: %s <input_path> <output_path> [transpose] ", argv[0]);
+    PRINT_MSG("[mode] [hop_size] [samplerate]\n");
+    PRINT_MSG(" with [transpose] a number of semi tones in the range "
+        " [-24, 24], and [mode] in 'default', 'crispness:0',"
+        " 'crispness:1', ... 'crispness:6'\n");
+    return err;
+  }
+
+#ifdef HAVE_RUBBERBAND
+  uint_t samplerate = 0;
+  uint_t hop_size = 64;
+  smpl_t transpose = 0.;
+  uint_t n_frames = 0, read = 0;
+
+  char_t *source_path = argv[1];
+  char_t *sink_path = argv[2];
+  char_t *mode = "default";
+
+  transpose = 0.;
+
+  if ( argc >= 4 ) transpose = atof(argv[3]);
+  if ( argc >= 5 ) mode = argv[4];
+  if ( argc >= 6 ) hop_size = atoi(argv[5]);
+  if ( argc >= 7 ) samplerate = atoi(argv[6]);
+
+  fvec_t *vec = new_fvec(hop_size);
+  fvec_t *out = new_fvec(hop_size);
+  if (!vec) { err = 1; goto beach_fvec; }
+
+  aubio_source_t *i = new_aubio_source(source_path, samplerate, hop_size);
+  if (!i) { err = 1; goto beach_source; }
+
+  if (samplerate == 0 ) samplerate = aubio_source_get_samplerate(i);
+
+  aubio_sink_t *o = new_aubio_sink(sink_path, samplerate);
+  if (!o) { err = 1; goto beach_sink; }
+
+  aubio_pitchshift_t *ps =
+    new_aubio_pitchshift(mode, transpose, hop_size, samplerate);
+  if (!ps) { err = 1; goto beach_pitchshift; }
+
+  do {
+    aubio_source_do(i, vec, &read);
+    //aubio_pitchshift_set_transpose(ps, tranpose);
+    aubio_pitchshift_do(ps, vec, out);
+    aubio_sink_do(o, out, read);
+    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("wrote to %s with hop_size: %d, samplerate: %d, latency %d\n",
+      sink_path, hop_size, samplerate, aubio_pitchshift_get_latency(ps));
+
+  del_aubio_pitchshift(ps);
+beach_pitchshift:
+  del_aubio_sink(o);
+beach_sink:
+  del_aubio_source(i);
+beach_source:
+  del_fvec(vec);
+  del_fvec(out);
+beach_fvec:
+  aubio_cleanup();
+#else
+  err = 0;
+  PRINT_ERR("aubio was not compiled with rubberband\n");
+#endif
+  return err;
+}
+
+int test_wrong_params(void)
+{
+  const char_t *mode = "default";
+  smpl_t transpose = 0.;
+  uint_t hop_size = 256;
+  uint_t samplerate = 44100;
+
+  if (new_aubio_pitchshift("??", transpose, hop_size, samplerate)) return 1;
+  if (new_aubio_pitchshift(mode,       28., hop_size, samplerate)) return 1;
+  if (new_aubio_pitchshift(mode, transpose,        0, samplerate)) return 1;
+  if (new_aubio_pitchshift(mode, transpose, hop_size,          0)) return 1;
+
+  aubio_pitchshift_t *p = new_aubio_pitchshift(mode, transpose,
+      hop_size, samplerate);
+#ifdef HAVE_RUBBERBAND
+  if (!p) return 1;
+  if (!aubio_pitchshift_set_pitchscale(p, 0.1)) return 1;
+  if (!aubio_pitchshift_set_transpose(p, -30)) return 1;
+  del_aubio_pitchshift(p);
+#else
+  if (p) return 1;
+#endif
+
+  return run_on_default_source_and_sink(main);
+}
diff --git a/tests/src/effects/test-timestretch.c b/tests/src/effects/test-timestretch.c
new file mode 100644 (file)
index 0000000..8ea48c2
--- /dev/null
@@ -0,0 +1,153 @@
+#define AUBIO_UNSTABLE 1
+#include <aubio.h>
+#include "utils_tests.h"
+
+int test_wrong_params(void);
+
+int main (int argc, char **argv)
+{
+  sint_t err = 0;
+
+  if (argc < 3 || argc >= 9) {
+    PRINT_ERR("wrong number of arguments, running tests\n");
+    err = test_wrong_params();
+    PRINT_MSG("usage: %s <input_path> <output_path> <stretch> [transpose] [mode] [hop_size] [samplerate]\n", argv[0]);
+    PRINT_MSG(" with <stretch> a time stretching ratio in the range [0.025, 10.]\n");
+    PRINT_MSG("      [transpose] a number of semi tones in the range [-24, 24]\n");
+    PRINT_MSG("  and [mode] in 'default', 'crispness:0', ..., 'crispness:6'\n");
+    return err;
+  }
+
+#ifdef HAVE_RUBBERBAND
+  uint_t samplerate = 0; // using source samplerate
+  uint_t hop_size = 64;
+  smpl_t transpose = 0.;
+  smpl_t stretch = 1.;
+  uint_t n_frames = 0, read = 0;
+  uint_t eof = 0, source_read = 0;
+
+  char_t *source_path = argv[1];
+  char_t *sink_path = argv[2];
+  char_t *mode = "default";
+
+  if ( argc >= 4 ) stretch = atof(argv[3]);
+  if ( argc >= 5 ) transpose = atof(argv[4]);
+  if ( argc >= 6 ) mode = argv[5];
+  if ( argc >= 7 ) hop_size = atoi(argv[6]);
+  if ( argc >= 8 ) samplerate = atoi(argv[7]);
+
+  uint_t source_hopsize = 2048;
+  aubio_source_t *s = new_aubio_source(source_path, samplerate, source_hopsize);
+  if (!s) { err = 1; goto beach_source; }
+  if (samplerate == 0) samplerate = aubio_source_get_samplerate(s);
+
+  fvec_t *in = new_fvec(source_hopsize);
+  fvec_t *out = new_fvec(hop_size);
+  if (!out || !in) { err = 1; goto beach_fvec; }
+
+  aubio_timestretch_t *ps = new_aubio_timestretch(mode, stretch, hop_size,
+      samplerate);
+  if (!ps) { err = 1; goto beach_timestretch; }
+  //if (samplerate == 0 ) samplerate = aubio_timestretch_get_samplerate(ps);
+
+  aubio_sink_t *o = new_aubio_sink(sink_path, samplerate);
+  if (!o) { err = 1; goto beach_sink; }
+
+  if (transpose != 0) aubio_timestretch_set_transpose(ps, transpose);
+
+  do {
+    //aubio_timestretch_set_stretch(ps, stretch);
+    //aubio_timestretch_set_transpose(ps, transpose);
+
+    while (aubio_timestretch_get_available(ps) < (sint_t)hop_size && !eof) {
+      aubio_source_do(s, in, &source_read);
+      aubio_timestretch_push(ps, in, source_read);
+      if (source_read < in->length) eof = 1;
+    }
+#if 0
+    if (n_frames == hop_size * 200) {
+      PRINT_MSG("sampler: setting stretch gave %d\n",
+          aubio_timestretch_set_stretch(ps, 2.) );
+      PRINT_MSG("sampler: getting stretch gave %f\n",
+          aubio_timestretch_get_stretch(ps) );
+      PRINT_MSG("sampler: setting transpose gave %d\n",
+          aubio_timestretch_set_transpose(ps, 12.) );
+      PRINT_MSG("sampler: getting transpose gave %f\n",
+          aubio_timestretch_get_transpose(ps) );
+    }
+#endif
+    aubio_timestretch_do(ps, out, &read);
+    aubio_sink_do(o, out, read);
+    n_frames += read;
+  } while ( read == hop_size );
+
+  PRINT_MSG("wrote %d frames at %dHz (%d blocks) from %s written to %s\n",
+      n_frames, samplerate, n_frames / hop_size,
+      source_path, sink_path);
+
+  del_aubio_sink(o);
+beach_sink:
+  del_aubio_timestretch(ps);
+beach_timestretch:
+  del_fvec(out);
+  del_fvec(in);
+beach_fvec:
+  del_aubio_source(s);
+beach_source:
+#else
+  err = 0;
+  PRINT_ERR("aubio was not compiled with rubberband\n");
+#endif
+  return err;
+}
+
+int test_wrong_params(void)
+{
+  const char_t *mode = "default";
+  smpl_t stretch = 1.;
+  uint_t hop_size = 256;
+  uint_t samplerate = 44100;
+
+  if (new_aubio_timestretch("ProcessOffline:?:", stretch, hop_size, samplerate)) return 1;
+  if (new_aubio_timestretch("", stretch, hop_size, samplerate)) return 1;
+  if (new_aubio_timestretch(mode,     41., hop_size, samplerate)) return 1;
+  if (new_aubio_timestretch(mode, stretch,        0, samplerate)) return 1;
+  if (new_aubio_timestretch(mode, stretch, hop_size,          0)) return 1;
+
+  aubio_timestretch_t *p = new_aubio_timestretch(mode, stretch, hop_size,
+      samplerate);
+#ifdef HAVE_RUBBERBAND
+  if (!p) return 1;
+
+  if (aubio_timestretch_get_latency(p) == 0) return 1;
+
+  if (aubio_timestretch_get_samplerate(p) != samplerate) return 1;
+
+  aubio_timestretch_reset(p);
+
+  if (aubio_timestretch_get_transpose(p) != 0) return 1;
+  if (aubio_timestretch_set_transpose(p, 2.)) return 1;
+  if (fabs(aubio_timestretch_get_transpose(p) - 2.) >= 1e-6) return 1;
+  if (!aubio_timestretch_set_transpose(p, 200.)) return 1;
+  if (!aubio_timestretch_set_transpose(p, -200.)) return 1;
+  if (aubio_timestretch_set_transpose(p, 0.)) return 1;
+
+  if (aubio_timestretch_get_pitchscale(p) != 1) return 1;
+  if (aubio_timestretch_set_pitchscale(p, 2.)) return 1;
+  if (fabs(aubio_timestretch_get_pitchscale(p) - 2.) >= 1e-6) return 1;
+  if (!aubio_timestretch_set_pitchscale(p, 0.)) return 1;
+  if (!aubio_timestretch_set_pitchscale(p, 6.)) return 1;
+
+  if (aubio_timestretch_get_stretch(p) != stretch) return 1;
+  if (aubio_timestretch_set_stretch(p, 2.)) return 1;
+  if (fabs(aubio_timestretch_get_stretch(p) - 2.) >= 1e-6) return 1;
+  if (!aubio_timestretch_set_stretch(p, 0.)) return 1;
+  if (!aubio_timestretch_set_stretch(p, 41.)) return 1;
+
+  del_aubio_timestretch(p);
+#else
+  if (p) return 1;
+#endif
+
+  return run_on_default_source_and_sink(main);
+}
diff --git a/tests/src/io/test-sink_flac.c b/tests/src/io/test-sink_flac.c
new file mode 100644 (file)
index 0000000..acc8428
--- /dev/null
@@ -0,0 +1,47 @@
+#define AUBIO_UNSTABLE 1
+#include <aubio.h>
+#include "utils_tests.h"
+
+#define aubio_sink_custom "flac"
+
+#ifdef HAVE_FLAC
+// functions not exposed in the headers, declared here
+typedef struct _aubio_sink_flac_t aubio_sink_flac_t;
+extern aubio_sink_flac_t * new_aubio_sink_flac(const char_t *uri,
+    uint_t samplerate);
+extern void del_aubio_sink_flac (aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_open(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_close(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
+    uint_t channels);
+extern uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
+    uint_t samplerate);
+extern void aubio_sink_flac_do(aubio_sink_flac_t *s, fvec_t* write_data,
+    uint_t write);
+extern void aubio_sink_flac_do_multi(aubio_sink_flac_t *s,
+    fmat_t *write_data, uint_t write);
+extern uint_t aubio_sink_flac_get_channels(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_get_samplerate(aubio_sink_flac_t *s);
+
+#define HAVE_AUBIO_SINK_CUSTOM
+#define aubio_sink_custom_t aubio_sink_flac_t
+#define new_aubio_sink_custom new_aubio_sink_flac
+#define del_aubio_sink_custom del_aubio_sink_flac
+#define aubio_sink_custom_do aubio_sink_flac_do
+#define aubio_sink_custom_do_multi aubio_sink_flac_do_multi
+#define aubio_sink_custom_close aubio_sink_flac_close
+#define aubio_sink_custom_preset_samplerate aubio_sink_flac_preset_samplerate
+#define aubio_sink_custom_preset_channels aubio_sink_flac_preset_channels
+#define aubio_sink_custom_get_samplerate aubio_sink_flac_get_samplerate
+#define aubio_sink_custom_get_channels aubio_sink_flac_get_channels
+#endif /* HAVE_FLAC */
+
+#include "base-sink_custom.h"
+
+// this file uses the unstable aubio api, please use aubio_sink instead
+// see src/io/sink.h and tests/src/sink/test-sink.c
+
+int main (int argc, char **argv)
+{
+  return base_main(argc, argv);
+}
diff --git a/tests/src/io/test-sink_vorbis.c b/tests/src/io/test-sink_vorbis.c
new file mode 100644 (file)
index 0000000..fe6bb2b
--- /dev/null
@@ -0,0 +1,47 @@
+#define AUBIO_UNSTABLE 1
+#include <aubio.h>
+#include "utils_tests.h"
+
+#define aubio_sink_custom "vorbis"
+
+#ifdef HAVE_VORBISENC
+// functions not exposed in the headers, declared here
+typedef struct _aubio_sink_vorbis_t aubio_sink_vorbis_t;
+extern aubio_sink_vorbis_t * new_aubio_sink_vorbis(const char_t *uri,
+    uint_t samplerate);
+extern void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_close(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
+    uint_t channels);
+extern uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
+    uint_t samplerate);
+extern void aubio_sink_vorbis_do(aubio_sink_vorbis_t *s, fvec_t *write_data,
+    uint_t write);
+extern void aubio_sink_vorbis_do_multi(aubio_sink_vorbis_t *s,
+    fmat_t *write_data, uint_t write);
+extern uint_t aubio_sink_vorbis_get_channels(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_get_samplerate(aubio_sink_vorbis_t *s);
+
+#define HAVE_AUBIO_SINK_CUSTOM
+#define aubio_sink_custom_t aubio_sink_vorbis_t
+#define new_aubio_sink_custom new_aubio_sink_vorbis
+#define del_aubio_sink_custom del_aubio_sink_vorbis
+#define aubio_sink_custom_do aubio_sink_vorbis_do
+#define aubio_sink_custom_do_multi aubio_sink_vorbis_do_multi
+#define aubio_sink_custom_close aubio_sink_vorbis_close
+#define aubio_sink_custom_preset_samplerate aubio_sink_vorbis_preset_samplerate
+#define aubio_sink_custom_preset_channels aubio_sink_vorbis_preset_channels
+#define aubio_sink_custom_get_samplerate aubio_sink_vorbis_get_samplerate
+#define aubio_sink_custom_get_channels aubio_sink_vorbis_get_channels
+#endif /* HAVE_VORBISENC */
+
+#include "base-sink_custom.h"
+
+// this file uses the unstable aubio api, please use aubio_sink instead
+// see src/io/sink.h and tests/src/sink/test-sink.c
+
+int main (int argc, char **argv)
+{
+  return base_main(argc, argv);
+}
index 3ea3109..8c6e89b 100644 (file)
@@ -1,5 +1,6 @@
 #include <math.h>
 #include "aubio.h"
+#include "aubio_priv.h"
 #include "utils_tests.h"
 
 int main (void)
@@ -32,7 +33,7 @@ int main (void)
     aubio_dct_do (dct, in, dctout);
     aubio_dct_rdo (dct, dctout, out);
     for (j = 0; j < in->length; j++) {
-      return_code += (fabsf(in->data[j] - out->data[j]) > 10.e-4);
+      return_code += (ABS(in->data[j] - out->data[j]) > 10.e-4);
     }
   }
 
diff --git a/wscript b/wscript
index dc15783..8ea98f3 100644 (file)
--- a/wscript
+++ b/wscript
@@ -11,6 +11,7 @@
 # For more info about waf, see http://code.google.com/p/waf/ .
 
 import sys
+import subprocess
 
 APPNAME = 'aubio'
 
@@ -48,6 +49,9 @@ def options(ctx):
     ctx.add_option('--debug', action = 'store_const',
             dest = 'build_type', const = 'debug',
             help = 'build in debug mode (see --build-type)')
+    ctx.add_option('--nodeps', action = 'store_const',
+            dest = 'nodeps', const = 'debug',
+            help = 'build with no external dependencies')
     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')
@@ -69,9 +73,18 @@ def options(ctx):
     add_option_enable_disable(ctx, 'avcodec', default = None,
             help_str = 'compile with libavcodec (auto)',
             help_disable_str = 'disable libavcodec')
+    add_option_enable_disable(ctx, 'vorbis', default = None,
+            help_str = 'compile with libvorbis (auto)',
+            help_disable_str = 'disable libvorbis')
+    add_option_enable_disable(ctx, 'flac', default = None,
+            help_str = 'compile with libFLAC (auto)',
+            help_disable_str = 'disable libflac')
     add_option_enable_disable(ctx, 'samplerate', default = None,
             help_str = 'compile with samplerate (auto)',
             help_disable_str = 'disable samplerate')
+    add_option_enable_disable(ctx, 'rubberband', default = None,
+            help_str = 'compile with rubberband (auto)',
+            help_disable_str = 'disable rubberband')
     add_option_enable_disable(ctx, 'memcpy', default = True,
             help_str = 'use memcpy hacks (default)',
             help_disable_str = 'do not use memcpy hacks')
@@ -126,6 +139,30 @@ def configure(ctx):
     if ctx.options.target_platform:
         target_platform = ctx.options.target_platform
 
+    if ctx.options.nodeps:
+        external_deps = [
+                'sndfile',
+                'samplerate',
+                'jack',
+                'rubberband',
+                'avcodec',
+                'blas',
+                'fftw3',
+                'fftw3f',
+                'flac',
+                'vorbis',
+        ]
+        for d in external_deps:
+            if not hasattr(ctx.options, 'enable_' + d):
+                raise ctx.errors.ConfigurationError ('--enable-%s missing from options' % d)
+            if getattr(ctx.options, 'enable_' + d) == True:
+                msg = 'Option --nodeps can not be used along with --enable-%s' % d
+                raise ctx.errors.ConfigurationError (msg)
+            elif getattr(ctx.options, 'enable_' + d) is None:
+                msg = 'Option --nodeps used but automatic detection with --enable-%s' % d
+                ctx.msg('Warning', msg)
+            setattr(ctx.options, 'enable_' + d, False)
+
     from waflib import Options
 
     if target_platform=='emscripten':
@@ -198,13 +235,13 @@ def configure(ctx):
         ctx.env['cshlib_PATTERN'] = 'lib%s.dll'
 
     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']
+        ctx.env.CFLAGS += ['-arch', 'arm64', '-arch', 'x86_64']
+        ctx.env.LINKFLAGS += ['-arch', 'arm64', '-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 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)
@@ -221,16 +258,28 @@ def configure(ctx):
             ctx.msg('Checking for Accelerate framework', 'no (disabled)',
                     color = 'YELLOW')
 
-    if target_platform in [ 'ios', 'iosimulator' ]:
+    if target_platform in [ 'ios', 'iosimulator', 'watchos', 'watchsimulator' ]:
         MINSDKVER="6.1"
+        xcodeslct_output = subprocess.check_output (['xcode-select', '--print-path'])
+        XCODEPATH = xcodeslct_output.decode(sys.stdout.encoding).strip()
+        if target_platform == 'ios':
+            SDKNAME = "iPhoneOS"
+        elif target_platform == 'iosimulator':
+            SDKNAME = "iPhoneSimulator"
+        elif target_platform == 'watchos':
+            SDKNAME = "WatchOS"
+        elif target_platform == 'watchsimulator':
+            SDKNAME = "WatchSimulator"
+        else:
+            raise ctx.errors.ConfigurationError ("Error: unknown target platform '"
+                + target_platform + "'")
+        DEVROOT = "%(XCODEPATH)s/Platforms/%(SDKNAME)s.platform/Developer" % locals()
+        SDKROOT = "%(DEVROOT)s/SDKs/%(SDKNAME)s.sdk" % locals()
         ctx.env.CFLAGS += ['-std=c99']
-        if (ctx.options.enable_apple_audio != False):
+        if ctx.options.enable_apple_audio != False and target_platform.startswith ('ios'):
             ctx.define('HAVE_AUDIO_UNIT', 1)
             #ctx.env.FRAMEWORK += ['CoreFoundation', 'AudioToolbox']
         if target_platform == 'ios':
-            DEVROOT = "/Applications/Xcode.app/Contents"
-            DEVROOT += "/Developer/Platforms/iPhoneOS.platform/Developer"
-            SDKROOT = "%(DEVROOT)s/SDKs/iPhoneOS.sdk" % locals()
             ctx.env.CFLAGS += [ '-fembed-bitcode' ]
             ctx.env.CFLAGS += [ '-arch', 'arm64' ]
             ctx.env.CFLAGS += [ '-arch', 'armv7' ]
@@ -240,16 +289,27 @@ def configure(ctx):
             ctx.env.LINKFLAGS += ['-arch', 'armv7s']
             ctx.env.CFLAGS += [ '-miphoneos-version-min=' + MINSDKVER ]
             ctx.env.LINKFLAGS += [ '-miphoneos-version-min=' + MINSDKVER ]
-        else:
-            DEVROOT = "/Applications/Xcode.app/Contents"
-            DEVROOT += "/Developer/Platforms/iPhoneSimulator.platform/Developer"
-            SDKROOT = "%(DEVROOT)s/SDKs/iPhoneSimulator.sdk" % locals()
-            ctx.env.CFLAGS += [ '-arch', 'i386' ]
+        elif target_platform == 'iosimulator':
             ctx.env.CFLAGS += [ '-arch', 'x86_64' ]
-            ctx.env.LINKFLAGS += ['-arch', 'i386']
+            ctx.env.CFLAGS += [ '-arch', 'arm64' ]
             ctx.env.LINKFLAGS += ['-arch', 'x86_64']
+            ctx.env.LINKFLAGS += ['-arch', 'arm64']
             ctx.env.CFLAGS += [ '-mios-simulator-version-min=' + MINSDKVER ]
             ctx.env.LINKFLAGS += [ '-mios-simulator-version-min=' + MINSDKVER ]
+        elif target_platform == 'watchos':
+            ctx.env.CFLAGS += [ '-arch', 'armv7' ]
+            ctx.env.CFLAGS += [ '-arch', 'armv7s' ]
+            ctx.env.LINKFLAGS += ['-arch', 'armv7']
+            ctx.env.LINKFLAGS += ['-arch', 'armv7s']
+            ctx.env.CFLAGS += [ '-mwatchos-version-min=' + MINSDKVER ]
+            ctx.env.LINKFLAGS += [ '-mwatchos-version-min=' + MINSDKVER ]
+        elif target_platform == 'watchsimulator':
+            ctx.env.CFLAGS += [ '-arch', 'x86_64' ]
+            ctx.env.CFLAGS += [ '-arch', 'arm64' ]
+            ctx.env.LINKFLAGS += ['-arch', 'x86_64']
+            ctx.env.LINKFLAGS += ['-arch', 'arm64']
+            ctx.env.CFLAGS += [ '-mwatchsimulator-version-min=' + MINSDKVER ]
+            ctx.env.LINKFLAGS += [ '-mwatchsimulator-version-min=' + MINSDKVER ]
         ctx.env.CFLAGS += [ '-isysroot' , SDKROOT]
         ctx.env.LINKFLAGS += [ '-isysroot' , SDKROOT]
 
@@ -384,6 +444,12 @@ def configure(ctx):
                 args = '--cflags --libs samplerate >= 0.0.15',
                 mandatory = ctx.options.enable_samplerate)
 
+    # check for librubberband
+    if (ctx.options.enable_rubberband != False):
+        ctx.check_cfg(package = 'rubberband', atleast_version = '1.3',
+                args = '--cflags --libs',
+                mandatory = ctx.options.enable_rubberband)
+
     # check for jack
     if (ctx.options.enable_jack != False):
         ctx.check_cfg(package = 'jack',
@@ -408,11 +474,6 @@ def configure(ctx):
                 args = '--cflags --libs libswresample >= 1.2.0',
                 uselib_store = 'SWRESAMPLE',
                 mandatory = False)
-        if 'HAVE_SWRESAMPLE' not in ctx.env:
-            ctx.check_cfg(package = 'libavresample',
-                    args = '--cflags --libs libavresample >= 1.0.1',
-                    uselib_store = 'AVRESAMPLE',
-                    mandatory = False)
 
         msg_check = 'Checking for all libav libraries'
         if 'HAVE_AVCODEC' not in ctx.env:
@@ -421,18 +482,27 @@ def configure(ctx):
             ctx.msg(msg_check, 'not found (missing avformat)', color = 'YELLOW')
         elif 'HAVE_AVUTIL' not in ctx.env:
             ctx.msg(msg_check, 'not found (missing avutil)', color = 'YELLOW')
-        elif 'HAVE_SWRESAMPLE' not in ctx.env \
-                and 'HAVE_AVRESAMPLE' not in ctx.env:
-            resample_missing = 'not found (avresample or swresample required)'
+        elif 'HAVE_SWRESAMPLE' not in ctx.env :
+            resample_missing = 'not found (missing swresample)'
             ctx.msg(msg_check, resample_missing, color = 'YELLOW')
         else:
             ctx.msg(msg_check, 'yes')
-            if 'HAVE_SWRESAMPLE' in ctx.env:
-                ctx.define('HAVE_SWRESAMPLE', 1)
-            elif 'HAVE_AVRESAMPLE' in ctx.env:
-                ctx.define('HAVE_AVRESAMPLE', 1)
             ctx.define('HAVE_LIBAV', 1)
 
+    # check for vorbisenc
+    if (ctx.options.enable_vorbis != False):
+        ctx.check_cfg(package = 'vorbisenc vorbis ogg',
+                args = '--cflags --libs',
+                uselib_store = 'VORBISENC',
+                mandatory = ctx.options.enable_vorbis)
+
+    # check for flac
+    if (ctx.options.enable_flac != False):
+        ctx.check_cfg(package = 'flac',
+                args = '--cflags --libs',
+                uselib_store = 'FLAC',
+                mandatory = ctx.options.enable_flac)
+
     if (ctx.options.enable_wavread != False):
         ctx.define('HAVE_WAVREAD', 1)
     ctx.msg('Checking if using source_wavread',
@@ -505,7 +575,7 @@ def build(bld):
     bld.recurse('src')
 
     # add sub directories
-    if bld.env['DEST_OS'] not in ['ios', 'iosimulator', 'android']:
+    if bld.env['DEST_OS'] not in ['ios', 'iosimulator', 'watchos', 'watchsimulator', 'android']:
         if bld.env['DEST_OS']=='emscripten' and not bld.options.testcmd:
             bld.options.testcmd = 'node %s'
         if bld.options.enable_examples: